162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * f_midi2.c -- USB MIDI 2.0 class function driver 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/device.h> 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <sound/core.h> 1262306a36Sopenharmony_ci#include <sound/control.h> 1362306a36Sopenharmony_ci#include <sound/ump.h> 1462306a36Sopenharmony_ci#include <sound/ump_msg.h> 1562306a36Sopenharmony_ci#include <sound/ump_convert.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/usb/ch9.h> 1862306a36Sopenharmony_ci#include <linux/usb/gadget.h> 1962306a36Sopenharmony_ci#include <linux/usb/audio.h> 2062306a36Sopenharmony_ci#include <linux/usb/midi-v2.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "u_f.h" 2362306a36Sopenharmony_ci#include "u_midi2.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct f_midi2; 2662306a36Sopenharmony_cistruct f_midi2_ep; 2762306a36Sopenharmony_cistruct f_midi2_usb_ep; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Context for each USB request */ 3062306a36Sopenharmony_cistruct f_midi2_req_ctx { 3162306a36Sopenharmony_ci struct f_midi2_usb_ep *usb_ep; /* belonging USB EP */ 3262306a36Sopenharmony_ci unsigned int index; /* array index: 0-31 */ 3362306a36Sopenharmony_ci struct usb_request *req; /* assigned request */ 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* Resources for a USB Endpoint */ 3762306a36Sopenharmony_cistruct f_midi2_usb_ep { 3862306a36Sopenharmony_ci struct f_midi2 *card; /* belonging card */ 3962306a36Sopenharmony_ci struct f_midi2_ep *ep; /* belonging UMP EP (optional) */ 4062306a36Sopenharmony_ci struct usb_ep *usb_ep; /* assigned USB EP */ 4162306a36Sopenharmony_ci void (*complete)(struct usb_ep *usb_ep, struct usb_request *req); 4262306a36Sopenharmony_ci unsigned long free_reqs; /* bitmap for unused requests */ 4362306a36Sopenharmony_ci unsigned int num_reqs; /* number of allocated requests */ 4462306a36Sopenharmony_ci struct f_midi2_req_ctx *reqs; /* request context array */ 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Resources for UMP Function Block (and USB Group Terminal Block) */ 4862306a36Sopenharmony_cistruct f_midi2_block { 4962306a36Sopenharmony_ci struct f_midi2_block_info info; /* FB info, copied from configfs */ 5062306a36Sopenharmony_ci struct snd_ump_block *fb; /* assigned FB */ 5162306a36Sopenharmony_ci unsigned int gtb_id; /* assigned GTB id */ 5262306a36Sopenharmony_ci unsigned int string_id; /* assigned string id */ 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* Temporary buffer for altset 0 MIDI 1.0 handling */ 5662306a36Sopenharmony_cistruct f_midi2_midi1_port { 5762306a36Sopenharmony_ci unsigned int pending; /* pending bytes on the input buffer */ 5862306a36Sopenharmony_ci u8 buf[32]; /* raw MIDI 1.0 byte input */ 5962306a36Sopenharmony_ci u8 state; /* running status */ 6062306a36Sopenharmony_ci u8 data[2]; /* rendered USB MIDI 1.0 packet data */ 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* MIDI 1.0 message states */ 6462306a36Sopenharmony_cienum { 6562306a36Sopenharmony_ci STATE_INITIAL = 0, /* pseudo state */ 6662306a36Sopenharmony_ci STATE_1PARAM, 6762306a36Sopenharmony_ci STATE_2PARAM_1, 6862306a36Sopenharmony_ci STATE_2PARAM_2, 6962306a36Sopenharmony_ci STATE_SYSEX_0, 7062306a36Sopenharmony_ci STATE_SYSEX_1, 7162306a36Sopenharmony_ci STATE_SYSEX_2, 7262306a36Sopenharmony_ci STATE_REAL_TIME, 7362306a36Sopenharmony_ci STATE_FINISHED, /* pseudo state */ 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* Resources for UMP Endpoint */ 7762306a36Sopenharmony_cistruct f_midi2_ep { 7862306a36Sopenharmony_ci struct snd_ump_endpoint *ump; /* assigned UMP EP */ 7962306a36Sopenharmony_ci struct f_midi2 *card; /* belonging MIDI 2.0 device */ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci struct f_midi2_ep_info info; /* UMP EP info, copied from configfs */ 8262306a36Sopenharmony_ci unsigned int num_blks; /* number of FBs */ 8362306a36Sopenharmony_ci struct f_midi2_block blks[SNDRV_UMP_MAX_BLOCKS]; /* UMP FBs */ 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci struct f_midi2_usb_ep ep_in; /* USB MIDI EP-in */ 8662306a36Sopenharmony_ci struct f_midi2_usb_ep ep_out; /* USB MIDI EP-out */ 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci u8 in_group_to_cable[SNDRV_UMP_MAX_GROUPS]; /* map to cable; 1-based! */ 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* indices for USB strings */ 9262306a36Sopenharmony_cienum { 9362306a36Sopenharmony_ci STR_IFACE = 0, 9462306a36Sopenharmony_ci STR_GTB1 = 1, 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* 1-based GTB id to string id */ 9862306a36Sopenharmony_ci#define gtb_to_str_id(id) (STR_GTB1 + (id) - 1) 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* mapping from MIDI 1.0 cable to UMP group */ 10162306a36Sopenharmony_cistruct midi1_cable_mapping { 10262306a36Sopenharmony_ci struct f_midi2_ep *ep; 10362306a36Sopenharmony_ci unsigned char block; 10462306a36Sopenharmony_ci unsigned char group; 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* operation mode */ 10862306a36Sopenharmony_cienum { 10962306a36Sopenharmony_ci MIDI_OP_MODE_UNSET, /* no altset set yet */ 11062306a36Sopenharmony_ci MIDI_OP_MODE_MIDI1, /* MIDI 1.0 (altset 0) is used */ 11162306a36Sopenharmony_ci MIDI_OP_MODE_MIDI2, /* MIDI 2.0 (altset 1) is used */ 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* Resources for MIDI 2.0 Device */ 11562306a36Sopenharmony_cistruct f_midi2 { 11662306a36Sopenharmony_ci struct usb_function func; 11762306a36Sopenharmony_ci struct usb_gadget *gadget; 11862306a36Sopenharmony_ci struct snd_card *card; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* MIDI 1.0 in/out USB EPs */ 12162306a36Sopenharmony_ci struct f_midi2_usb_ep midi1_ep_in; 12262306a36Sopenharmony_ci struct f_midi2_usb_ep midi1_ep_out; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* number of MIDI 1.0 I/O cables */ 12562306a36Sopenharmony_ci unsigned int num_midi1_in; 12662306a36Sopenharmony_ci unsigned int num_midi1_out; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* conversion for MIDI 1.0 EP-in */ 12962306a36Sopenharmony_ci struct f_midi2_midi1_port midi1_port[MAX_CABLES]; 13062306a36Sopenharmony_ci /* conversion for MIDI 1.0 EP-out */ 13162306a36Sopenharmony_ci struct ump_cvt_to_ump midi1_ump_cvt; 13262306a36Sopenharmony_ci /* mapping between cables and UMP groups */ 13362306a36Sopenharmony_ci struct midi1_cable_mapping in_cable_mapping[MAX_CABLES]; 13462306a36Sopenharmony_ci struct midi1_cable_mapping out_cable_mapping[MAX_CABLES]; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci int midi_if; /* USB MIDI interface number */ 13762306a36Sopenharmony_ci int operation_mode; /* current operation mode */ 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci spinlock_t queue_lock; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci struct f_midi2_card_info info; /* card info, copied from configfs */ 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci unsigned int num_eps; 14462306a36Sopenharmony_ci struct f_midi2_ep midi2_eps[MAX_UMP_EPS]; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci unsigned int total_blocks; /* total number of blocks of all EPs */ 14762306a36Sopenharmony_ci struct usb_string *string_defs; 14862306a36Sopenharmony_ci struct usb_string *strings; 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci#define func_to_midi2(f) container_of(f, struct f_midi2, func) 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* get EP name string */ 15462306a36Sopenharmony_cistatic const char *ump_ep_name(const struct f_midi2_ep *ep) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci return ep->info.ep_name ? ep->info.ep_name : "MIDI 2.0 Gadget"; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* get EP product ID string */ 16062306a36Sopenharmony_cistatic const char *ump_product_id(const struct f_midi2_ep *ep) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci return ep->info.product_id ? ep->info.product_id : "Unique Product ID"; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* get FB name string */ 16662306a36Sopenharmony_cistatic const char *ump_fb_name(const struct f_midi2_block_info *info) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci return info->name ? info->name : "MIDI 2.0 Gadget I/O"; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/* 17262306a36Sopenharmony_ci * USB Descriptor Definitions 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci/* GTB header descriptor */ 17562306a36Sopenharmony_cistatic struct usb_ms20_gr_trm_block_header_descriptor gtb_header_desc = { 17662306a36Sopenharmony_ci .bLength = sizeof(gtb_header_desc), 17762306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_GR_TRM_BLOCK, 17862306a36Sopenharmony_ci .bDescriptorSubtype = USB_MS_GR_TRM_BLOCK_HEADER, 17962306a36Sopenharmony_ci .wTotalLength = __cpu_to_le16(0x12), // to be filled 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* GTB descriptor template: most items are replaced dynamically */ 18362306a36Sopenharmony_cistatic struct usb_ms20_gr_trm_block_descriptor gtb_desc = { 18462306a36Sopenharmony_ci .bLength = sizeof(gtb_desc), 18562306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_GR_TRM_BLOCK, 18662306a36Sopenharmony_ci .bDescriptorSubtype = USB_MS_GR_TRM_BLOCK, 18762306a36Sopenharmony_ci .bGrpTrmBlkID = 0x01, 18862306a36Sopenharmony_ci .bGrpTrmBlkType = USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL, 18962306a36Sopenharmony_ci .nGroupTrm = 0x00, 19062306a36Sopenharmony_ci .nNumGroupTrm = 1, 19162306a36Sopenharmony_ci .iBlockItem = 0, 19262306a36Sopenharmony_ci .bMIDIProtocol = USB_MS_MIDI_PROTO_1_0_64, 19362306a36Sopenharmony_ci .wMaxInputBandwidth = 0, 19462306a36Sopenharmony_ci .wMaxOutputBandwidth = 0, 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ciDECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); 19862306a36Sopenharmony_ciDECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16); 19962306a36Sopenharmony_ciDECLARE_UAC_AC_HEADER_DESCRIPTOR(1); 20062306a36Sopenharmony_ciDECLARE_USB_MS20_ENDPOINT_DESCRIPTOR(32); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci#define EP_MAX_PACKET_INT 8 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* Audio Control Interface */ 20562306a36Sopenharmony_cistatic struct usb_interface_descriptor midi2_audio_if_desc = { 20662306a36Sopenharmony_ci .bLength = USB_DT_INTERFACE_SIZE, 20762306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 20862306a36Sopenharmony_ci .bInterfaceNumber = 0, // to be filled 20962306a36Sopenharmony_ci .bNumEndpoints = 0, 21062306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_AUDIO, 21162306a36Sopenharmony_ci .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, 21262306a36Sopenharmony_ci .bInterfaceProtocol = 0, 21362306a36Sopenharmony_ci .iInterface = 0, 21462306a36Sopenharmony_ci}; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic struct uac1_ac_header_descriptor_1 midi2_audio_class_desc = { 21762306a36Sopenharmony_ci .bLength = 0x09, 21862306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 21962306a36Sopenharmony_ci .bDescriptorSubtype = 0x01, 22062306a36Sopenharmony_ci .bcdADC = __cpu_to_le16(0x0100), 22162306a36Sopenharmony_ci .wTotalLength = __cpu_to_le16(0x0009), 22262306a36Sopenharmony_ci .bInCollection = 0x01, 22362306a36Sopenharmony_ci .baInterfaceNr = { 0x01 }, // to be filled 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* MIDI 1.0 Streaming Interface (altset 0) */ 22762306a36Sopenharmony_cistatic struct usb_interface_descriptor midi2_midi1_if_desc = { 22862306a36Sopenharmony_ci .bLength = USB_DT_INTERFACE_SIZE, 22962306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 23062306a36Sopenharmony_ci .bInterfaceNumber = 0, // to be filled 23162306a36Sopenharmony_ci .bAlternateSetting = 0, 23262306a36Sopenharmony_ci .bNumEndpoints = 2, // to be filled 23362306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_AUDIO, 23462306a36Sopenharmony_ci .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, 23562306a36Sopenharmony_ci .bInterfaceProtocol = 0, 23662306a36Sopenharmony_ci .iInterface = 0, // to be filled 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic struct usb_ms_header_descriptor midi2_midi1_class_desc = { 24062306a36Sopenharmony_ci .bLength = 0x07, 24162306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 24262306a36Sopenharmony_ci .bDescriptorSubtype = USB_MS_HEADER, 24362306a36Sopenharmony_ci .bcdMSC = __cpu_to_le16(0x0100), 24462306a36Sopenharmony_ci .wTotalLength = __cpu_to_le16(0x41), // to be calculated 24562306a36Sopenharmony_ci}; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* MIDI 1.0 EP OUT */ 24862306a36Sopenharmony_cistatic struct usb_endpoint_descriptor midi2_midi1_ep_out_desc = { 24962306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, 25062306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 25162306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT | 0, // set up dynamically 25262306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 25362306a36Sopenharmony_ci}; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor midi2_midi1_ep_out_ss_comp_desc = { 25662306a36Sopenharmony_ci .bLength = sizeof(midi2_midi1_ep_out_ss_comp_desc), 25762306a36Sopenharmony_ci .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 25862306a36Sopenharmony_ci}; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_out_class_desc = { 26162306a36Sopenharmony_ci .bLength = 0x05, // to be filled 26262306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_ENDPOINT, 26362306a36Sopenharmony_ci .bDescriptorSubtype = USB_MS_GENERAL, 26462306a36Sopenharmony_ci .bNumEmbMIDIJack = 1, 26562306a36Sopenharmony_ci .baAssocJackID = { 0x01 }, 26662306a36Sopenharmony_ci}; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* MIDI 1.0 EP IN */ 26962306a36Sopenharmony_cistatic struct usb_endpoint_descriptor midi2_midi1_ep_in_desc = { 27062306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, 27162306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 27262306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN | 0, // set up dynamically 27362306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 27462306a36Sopenharmony_ci}; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor midi2_midi1_ep_in_ss_comp_desc = { 27762306a36Sopenharmony_ci .bLength = sizeof(midi2_midi1_ep_in_ss_comp_desc), 27862306a36Sopenharmony_ci .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 27962306a36Sopenharmony_ci}; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_in_class_desc = { 28262306a36Sopenharmony_ci .bLength = 0x05, // to be filled 28362306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_ENDPOINT, 28462306a36Sopenharmony_ci .bDescriptorSubtype = USB_MS_GENERAL, 28562306a36Sopenharmony_ci .bNumEmbMIDIJack = 1, 28662306a36Sopenharmony_ci .baAssocJackID = { 0x03 }, 28762306a36Sopenharmony_ci}; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci/* MIDI 2.0 Streaming Interface (altset 1) */ 29062306a36Sopenharmony_cistatic struct usb_interface_descriptor midi2_midi2_if_desc = { 29162306a36Sopenharmony_ci .bLength = USB_DT_INTERFACE_SIZE, 29262306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 29362306a36Sopenharmony_ci .bInterfaceNumber = 0, // to be filled 29462306a36Sopenharmony_ci .bAlternateSetting = 1, 29562306a36Sopenharmony_ci .bNumEndpoints = 2, // to be filled 29662306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_AUDIO, 29762306a36Sopenharmony_ci .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, 29862306a36Sopenharmony_ci .bInterfaceProtocol = 0, 29962306a36Sopenharmony_ci .iInterface = 0, // to be filled 30062306a36Sopenharmony_ci}; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic struct usb_ms_header_descriptor midi2_midi2_class_desc = { 30362306a36Sopenharmony_ci .bLength = 0x07, 30462306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 30562306a36Sopenharmony_ci .bDescriptorSubtype = USB_MS_HEADER, 30662306a36Sopenharmony_ci .bcdMSC = __cpu_to_le16(0x0200), 30762306a36Sopenharmony_ci .wTotalLength = __cpu_to_le16(0x07), 30862306a36Sopenharmony_ci}; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/* MIDI 2.0 EP OUT */ 31162306a36Sopenharmony_cistatic struct usb_endpoint_descriptor midi2_midi2_ep_out_desc[MAX_UMP_EPS]; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor midi2_midi2_ep_out_ss_comp_desc = { 31462306a36Sopenharmony_ci .bLength = sizeof(midi2_midi1_ep_out_ss_comp_desc), 31562306a36Sopenharmony_ci .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 31662306a36Sopenharmony_ci}; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_out_class_desc[MAX_UMP_EPS]; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci/* MIDI 2.0 EP IN */ 32162306a36Sopenharmony_cistatic struct usb_endpoint_descriptor midi2_midi2_ep_in_desc[MAX_UMP_EPS]; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor midi2_midi2_ep_in_ss_comp_desc = { 32462306a36Sopenharmony_ci .bLength = sizeof(midi2_midi2_ep_in_ss_comp_desc), 32562306a36Sopenharmony_ci .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 32662306a36Sopenharmony_ci}; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_in_class_desc[MAX_UMP_EPS]; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci/* Arrays of descriptors to be created */ 33162306a36Sopenharmony_cistatic void *midi2_audio_descs[] = { 33262306a36Sopenharmony_ci &midi2_audio_if_desc, 33362306a36Sopenharmony_ci &midi2_audio_class_desc, 33462306a36Sopenharmony_ci NULL 33562306a36Sopenharmony_ci}; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void *midi2_midi1_descs[] = { 33862306a36Sopenharmony_ci &midi2_midi1_if_desc, 33962306a36Sopenharmony_ci &midi2_midi1_class_desc, 34062306a36Sopenharmony_ci NULL 34162306a36Sopenharmony_ci}; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic void *midi2_midi1_ep_out_descs[] = { 34462306a36Sopenharmony_ci &midi2_midi1_ep_out_desc, 34562306a36Sopenharmony_ci &midi2_midi1_ep_out_class_desc, 34662306a36Sopenharmony_ci NULL 34762306a36Sopenharmony_ci}; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void *midi2_midi1_ep_in_descs[] = { 35062306a36Sopenharmony_ci &midi2_midi1_ep_in_desc, 35162306a36Sopenharmony_ci &midi2_midi1_ep_in_class_desc, 35262306a36Sopenharmony_ci NULL 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic void *midi2_midi1_ep_out_ss_descs[] = { 35662306a36Sopenharmony_ci &midi2_midi1_ep_out_desc, 35762306a36Sopenharmony_ci &midi2_midi1_ep_out_ss_comp_desc, 35862306a36Sopenharmony_ci &midi2_midi1_ep_out_class_desc, 35962306a36Sopenharmony_ci NULL 36062306a36Sopenharmony_ci}; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic void *midi2_midi1_ep_in_ss_descs[] = { 36362306a36Sopenharmony_ci &midi2_midi1_ep_in_desc, 36462306a36Sopenharmony_ci &midi2_midi1_ep_in_ss_comp_desc, 36562306a36Sopenharmony_ci &midi2_midi1_ep_in_class_desc, 36662306a36Sopenharmony_ci NULL 36762306a36Sopenharmony_ci}; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic void *midi2_midi2_descs[] = { 37062306a36Sopenharmony_ci &midi2_midi2_if_desc, 37162306a36Sopenharmony_ci &midi2_midi2_class_desc, 37262306a36Sopenharmony_ci NULL 37362306a36Sopenharmony_ci}; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/* 37662306a36Sopenharmony_ci * USB request handling 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/* get an empty request for the given EP */ 38062306a36Sopenharmony_cistatic struct usb_request *get_empty_request(struct f_midi2_usb_ep *usb_ep) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct usb_request *req = NULL; 38362306a36Sopenharmony_ci unsigned long flags; 38462306a36Sopenharmony_ci int index; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci spin_lock_irqsave(&usb_ep->card->queue_lock, flags); 38762306a36Sopenharmony_ci if (!usb_ep->free_reqs) 38862306a36Sopenharmony_ci goto unlock; 38962306a36Sopenharmony_ci index = find_first_bit(&usb_ep->free_reqs, usb_ep->num_reqs); 39062306a36Sopenharmony_ci if (index >= usb_ep->num_reqs) 39162306a36Sopenharmony_ci goto unlock; 39262306a36Sopenharmony_ci req = usb_ep->reqs[index].req; 39362306a36Sopenharmony_ci if (!req) 39462306a36Sopenharmony_ci goto unlock; 39562306a36Sopenharmony_ci clear_bit(index, &usb_ep->free_reqs); 39662306a36Sopenharmony_ci req->length = 0; 39762306a36Sopenharmony_ci unlock: 39862306a36Sopenharmony_ci spin_unlock_irqrestore(&usb_ep->card->queue_lock, flags); 39962306a36Sopenharmony_ci return req; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci/* put the empty request back */ 40362306a36Sopenharmony_cistatic void put_empty_request(struct usb_request *req) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct f_midi2_req_ctx *ctx = req->context; 40662306a36Sopenharmony_ci unsigned long flags; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci spin_lock_irqsave(&ctx->usb_ep->card->queue_lock, flags); 40962306a36Sopenharmony_ci set_bit(ctx->index, &ctx->usb_ep->free_reqs); 41062306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->usb_ep->card->queue_lock, flags); 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci/* 41462306a36Sopenharmony_ci * UMP v1.1 Stream message handling 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/* queue a request to UMP EP; request is either queued or freed after this */ 41862306a36Sopenharmony_cistatic int queue_request_ep_raw(struct usb_request *req) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct f_midi2_req_ctx *ctx = req->context; 42162306a36Sopenharmony_ci int err; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci req->complete = ctx->usb_ep->complete; 42462306a36Sopenharmony_ci err = usb_ep_queue(ctx->usb_ep->usb_ep, req, GFP_ATOMIC); 42562306a36Sopenharmony_ci if (err) { 42662306a36Sopenharmony_ci put_empty_request(req); 42762306a36Sopenharmony_ci return err; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci/* queue a request with endianness conversion */ 43362306a36Sopenharmony_cistatic int queue_request_ep_in(struct usb_request *req) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci /* UMP packets have to be converted to little-endian */ 43662306a36Sopenharmony_ci cpu_to_le32_array((u32 *)req->buf, req->length >> 2); 43762306a36Sopenharmony_ci return queue_request_ep_raw(req); 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci/* reply a UMP packet via EP-in */ 44162306a36Sopenharmony_cistatic int reply_ep_in(struct f_midi2_ep *ep, const void *buf, int len) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct f_midi2_usb_ep *usb_ep = &ep->ep_in; 44462306a36Sopenharmony_ci struct usb_request *req; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci req = get_empty_request(usb_ep); 44762306a36Sopenharmony_ci if (!req) 44862306a36Sopenharmony_ci return -ENOSPC; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci req->length = len; 45162306a36Sopenharmony_ci memcpy(req->buf, buf, len); 45262306a36Sopenharmony_ci return queue_request_ep_in(req); 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci/* reply a UMP stream EP info */ 45662306a36Sopenharmony_cistatic void reply_ump_stream_ep_info(struct f_midi2_ep *ep) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct snd_ump_stream_msg_ep_info rep = { 45962306a36Sopenharmony_ci .type = UMP_MSG_TYPE_STREAM, 46062306a36Sopenharmony_ci .status = UMP_STREAM_MSG_STATUS_EP_INFO, 46162306a36Sopenharmony_ci .ump_version_major = 0x01, 46262306a36Sopenharmony_ci .ump_version_minor = 0x01, 46362306a36Sopenharmony_ci .num_function_blocks = ep->num_blks, 46462306a36Sopenharmony_ci .static_function_block = !!ep->card->info.static_block, 46562306a36Sopenharmony_ci .protocol = (UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 | 46662306a36Sopenharmony_ci UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) >> 8, 46762306a36Sopenharmony_ci }; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci reply_ep_in(ep, &rep, sizeof(rep)); 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci/* reply a UMP EP device info */ 47362306a36Sopenharmony_cistatic void reply_ump_stream_ep_device(struct f_midi2_ep *ep) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct snd_ump_stream_msg_devince_info rep = { 47662306a36Sopenharmony_ci .type = UMP_MSG_TYPE_STREAM, 47762306a36Sopenharmony_ci .status = UMP_STREAM_MSG_STATUS_DEVICE_INFO, 47862306a36Sopenharmony_ci .manufacture_id = ep->info.manufacturer, 47962306a36Sopenharmony_ci .family_lsb = ep->info.family & 0xff, 48062306a36Sopenharmony_ci .family_msb = (ep->info.family >> 8) & 0xff, 48162306a36Sopenharmony_ci .model_lsb = ep->info.model & 0xff, 48262306a36Sopenharmony_ci .model_msb = (ep->info.model >> 8) & 0xff, 48362306a36Sopenharmony_ci .sw_revision = ep->info.sw_revision, 48462306a36Sopenharmony_ci }; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci reply_ep_in(ep, &rep, sizeof(rep)); 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci#define UMP_STREAM_PKT_BYTES 16 /* UMP stream packet size = 16 bytes*/ 49062306a36Sopenharmony_ci#define UMP_STREAM_EP_STR_OFF 2 /* offset of name string for EP info */ 49162306a36Sopenharmony_ci#define UMP_STREAM_FB_STR_OFF 3 /* offset of name string for FB info */ 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci/* Helper to replay a string */ 49462306a36Sopenharmony_cistatic void reply_ump_stream_string(struct f_midi2_ep *ep, const u8 *name, 49562306a36Sopenharmony_ci unsigned int type, unsigned int extra, 49662306a36Sopenharmony_ci unsigned int start_ofs) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct f_midi2_usb_ep *usb_ep = &ep->ep_in; 49962306a36Sopenharmony_ci struct f_midi2 *midi2 = ep->card; 50062306a36Sopenharmony_ci struct usb_request *req; 50162306a36Sopenharmony_ci unsigned int pos; 50262306a36Sopenharmony_ci u32 *buf; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (!*name) 50562306a36Sopenharmony_ci return; 50662306a36Sopenharmony_ci req = get_empty_request(usb_ep); 50762306a36Sopenharmony_ci if (!req) 50862306a36Sopenharmony_ci return; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci buf = (u32 *)req->buf; 51162306a36Sopenharmony_ci pos = start_ofs; 51262306a36Sopenharmony_ci for (;;) { 51362306a36Sopenharmony_ci if (pos == start_ofs) { 51462306a36Sopenharmony_ci memset(buf, 0, UMP_STREAM_PKT_BYTES); 51562306a36Sopenharmony_ci buf[0] = ump_stream_compose(type, 0) | extra; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci buf[pos / 4] |= *name++ << ((3 - (pos % 4)) * 8); 51862306a36Sopenharmony_ci if (!*name) { 51962306a36Sopenharmony_ci if (req->length) 52062306a36Sopenharmony_ci buf[0] |= UMP_STREAM_MSG_FORMAT_END << 26; 52162306a36Sopenharmony_ci req->length += UMP_STREAM_PKT_BYTES; 52262306a36Sopenharmony_ci break; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci if (++pos == UMP_STREAM_PKT_BYTES) { 52562306a36Sopenharmony_ci if (!req->length) 52662306a36Sopenharmony_ci buf[0] |= UMP_STREAM_MSG_FORMAT_START << 26; 52762306a36Sopenharmony_ci else 52862306a36Sopenharmony_ci buf[0] |= UMP_STREAM_MSG_FORMAT_CONTINUE << 26; 52962306a36Sopenharmony_ci req->length += UMP_STREAM_PKT_BYTES; 53062306a36Sopenharmony_ci if (midi2->info.req_buf_size - req->length < UMP_STREAM_PKT_BYTES) 53162306a36Sopenharmony_ci break; 53262306a36Sopenharmony_ci buf += 4; 53362306a36Sopenharmony_ci pos = start_ofs; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (req->length) 53862306a36Sopenharmony_ci queue_request_ep_in(req); 53962306a36Sopenharmony_ci else 54062306a36Sopenharmony_ci put_empty_request(req); 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci/* Reply a UMP EP name string */ 54462306a36Sopenharmony_cistatic void reply_ump_stream_ep_name(struct f_midi2_ep *ep) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci reply_ump_stream_string(ep, ump_ep_name(ep), 54762306a36Sopenharmony_ci UMP_STREAM_MSG_STATUS_EP_NAME, 0, 54862306a36Sopenharmony_ci UMP_STREAM_EP_STR_OFF); 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci/* Reply a UMP EP product ID string */ 55262306a36Sopenharmony_cistatic void reply_ump_stream_ep_pid(struct f_midi2_ep *ep) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci reply_ump_stream_string(ep, ump_product_id(ep), 55562306a36Sopenharmony_ci UMP_STREAM_MSG_STATUS_PRODUCT_ID, 0, 55662306a36Sopenharmony_ci UMP_STREAM_EP_STR_OFF); 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci/* Reply a UMP EP stream config */ 56062306a36Sopenharmony_cistatic void reply_ump_stream_ep_config(struct f_midi2_ep *ep) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci struct snd_ump_stream_msg_stream_cfg rep = { 56362306a36Sopenharmony_ci .type = UMP_MSG_TYPE_STREAM, 56462306a36Sopenharmony_ci .status = UMP_STREAM_MSG_STATUS_STREAM_CFG, 56562306a36Sopenharmony_ci }; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if ((ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK) == 56862306a36Sopenharmony_ci SNDRV_UMP_EP_INFO_PROTO_MIDI2) 56962306a36Sopenharmony_ci rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI2 >> 8; 57062306a36Sopenharmony_ci else 57162306a36Sopenharmony_ci rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 >> 8; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci reply_ep_in(ep, &rep, sizeof(rep)); 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci/* Reply a UMP FB info */ 57762306a36Sopenharmony_cistatic void reply_ump_stream_fb_info(struct f_midi2_ep *ep, int blk) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct f_midi2_block_info *b = &ep->blks[blk].info; 58062306a36Sopenharmony_ci struct snd_ump_stream_msg_fb_info rep = { 58162306a36Sopenharmony_ci .type = UMP_MSG_TYPE_STREAM, 58262306a36Sopenharmony_ci .status = UMP_STREAM_MSG_STATUS_FB_INFO, 58362306a36Sopenharmony_ci .active = !!b->active, 58462306a36Sopenharmony_ci .function_block_id = blk, 58562306a36Sopenharmony_ci .ui_hint = b->ui_hint, 58662306a36Sopenharmony_ci .midi_10 = b->is_midi1, 58762306a36Sopenharmony_ci .direction = b->direction, 58862306a36Sopenharmony_ci .first_group = b->first_group, 58962306a36Sopenharmony_ci .num_groups = b->num_groups, 59062306a36Sopenharmony_ci .midi_ci_version = b->midi_ci_version, 59162306a36Sopenharmony_ci .sysex8_streams = b->sysex8_streams, 59262306a36Sopenharmony_ci }; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci reply_ep_in(ep, &rep, sizeof(rep)); 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci/* Reply a FB name string */ 59862306a36Sopenharmony_cistatic void reply_ump_stream_fb_name(struct f_midi2_ep *ep, unsigned int blk) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci reply_ump_stream_string(ep, ump_fb_name(&ep->blks[blk].info), 60162306a36Sopenharmony_ci UMP_STREAM_MSG_STATUS_FB_NAME, blk << 8, 60262306a36Sopenharmony_ci UMP_STREAM_FB_STR_OFF); 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci/* Process a UMP Stream message */ 60662306a36Sopenharmony_cistatic void process_ump_stream_msg(struct f_midi2_ep *ep, const u32 *data) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct f_midi2 *midi2 = ep->card; 60962306a36Sopenharmony_ci unsigned int format, status, blk; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci format = ump_stream_message_format(*data); 61262306a36Sopenharmony_ci status = ump_stream_message_status(*data); 61362306a36Sopenharmony_ci switch (status) { 61462306a36Sopenharmony_ci case UMP_STREAM_MSG_STATUS_EP_DISCOVERY: 61562306a36Sopenharmony_ci if (format) 61662306a36Sopenharmony_ci return; // invalid 61762306a36Sopenharmony_ci if (data[1] & UMP_STREAM_MSG_REQUEST_EP_INFO) 61862306a36Sopenharmony_ci reply_ump_stream_ep_info(ep); 61962306a36Sopenharmony_ci if (data[1] & UMP_STREAM_MSG_REQUEST_DEVICE_INFO) 62062306a36Sopenharmony_ci reply_ump_stream_ep_device(ep); 62162306a36Sopenharmony_ci if (data[1] & UMP_STREAM_MSG_REQUEST_EP_NAME) 62262306a36Sopenharmony_ci reply_ump_stream_ep_name(ep); 62362306a36Sopenharmony_ci if (data[1] & UMP_STREAM_MSG_REQUEST_PRODUCT_ID) 62462306a36Sopenharmony_ci reply_ump_stream_ep_pid(ep); 62562306a36Sopenharmony_ci if (data[1] & UMP_STREAM_MSG_REQUEST_STREAM_CFG) 62662306a36Sopenharmony_ci reply_ump_stream_ep_config(ep); 62762306a36Sopenharmony_ci return; 62862306a36Sopenharmony_ci case UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST: 62962306a36Sopenharmony_ci if (*data & UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) { 63062306a36Sopenharmony_ci ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI2; 63162306a36Sopenharmony_ci DBG(midi2, "Switching Protocol to MIDI2\n"); 63262306a36Sopenharmony_ci } else { 63362306a36Sopenharmony_ci ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1; 63462306a36Sopenharmony_ci DBG(midi2, "Switching Protocol to MIDI1\n"); 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci snd_ump_switch_protocol(ep->ump, ep->info.protocol); 63762306a36Sopenharmony_ci reply_ump_stream_ep_config(ep); 63862306a36Sopenharmony_ci return; 63962306a36Sopenharmony_ci case UMP_STREAM_MSG_STATUS_FB_DISCOVERY: 64062306a36Sopenharmony_ci if (format) 64162306a36Sopenharmony_ci return; // invalid 64262306a36Sopenharmony_ci blk = (*data >> 8) & 0xff; 64362306a36Sopenharmony_ci if (blk >= ep->num_blks) 64462306a36Sopenharmony_ci return; 64562306a36Sopenharmony_ci if (*data & UMP_STREAM_MSG_REQUEST_FB_INFO) 64662306a36Sopenharmony_ci reply_ump_stream_fb_info(ep, blk); 64762306a36Sopenharmony_ci if (*data & UMP_STREAM_MSG_REQUEST_FB_NAME) 64862306a36Sopenharmony_ci reply_ump_stream_fb_name(ep, blk); 64962306a36Sopenharmony_ci return; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci/* Process UMP messages included in a USB request */ 65462306a36Sopenharmony_cistatic void process_ump(struct f_midi2_ep *ep, const struct usb_request *req) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci const u32 *data = (u32 *)req->buf; 65762306a36Sopenharmony_ci int len = req->actual >> 2; 65862306a36Sopenharmony_ci const u32 *in_buf = ep->ump->input_buf; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci for (; len > 0; len--, data++) { 66162306a36Sopenharmony_ci if (snd_ump_receive_ump_val(ep->ump, *data) <= 0) 66262306a36Sopenharmony_ci continue; 66362306a36Sopenharmony_ci if (ump_message_type(*in_buf) == UMP_MSG_TYPE_STREAM) 66462306a36Sopenharmony_ci process_ump_stream_msg(ep, in_buf); 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci/* 66962306a36Sopenharmony_ci * MIDI 2.0 UMP USB request handling 67062306a36Sopenharmony_ci */ 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci/* complete handler for UMP EP-out requests */ 67362306a36Sopenharmony_cistatic void f_midi2_ep_out_complete(struct usb_ep *usb_ep, 67462306a36Sopenharmony_ci struct usb_request *req) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci struct f_midi2_req_ctx *ctx = req->context; 67762306a36Sopenharmony_ci struct f_midi2_ep *ep = ctx->usb_ep->ep; 67862306a36Sopenharmony_ci struct f_midi2 *midi2 = ep->card; 67962306a36Sopenharmony_ci int status = req->status; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (status) { 68262306a36Sopenharmony_ci DBG(midi2, "%s complete error %d: %d/%d\n", 68362306a36Sopenharmony_ci usb_ep->name, status, req->actual, req->length); 68462306a36Sopenharmony_ci goto error; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* convert to UMP packet in native endianness */ 68862306a36Sopenharmony_ci le32_to_cpu_array((u32 *)req->buf, req->actual >> 2); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci if (midi2->info.process_ump) 69162306a36Sopenharmony_ci process_ump(ep, req); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci snd_ump_receive(ep->ump, req->buf, req->actual & ~3); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci if (midi2->operation_mode != MIDI_OP_MODE_MIDI2) 69662306a36Sopenharmony_ci goto error; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (queue_request_ep_raw(req)) 69962306a36Sopenharmony_ci goto error; 70062306a36Sopenharmony_ci return; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci error: 70362306a36Sopenharmony_ci put_empty_request(req); 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci/* Transmit UMP packets received from user-space to the gadget */ 70762306a36Sopenharmony_cistatic void process_ump_transmit(struct f_midi2_ep *ep) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci struct f_midi2_usb_ep *usb_ep = &ep->ep_in; 71062306a36Sopenharmony_ci struct f_midi2 *midi2 = ep->card; 71162306a36Sopenharmony_ci struct usb_request *req; 71262306a36Sopenharmony_ci int len; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (!usb_ep->usb_ep->enabled) 71562306a36Sopenharmony_ci return; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci for (;;) { 71862306a36Sopenharmony_ci req = get_empty_request(usb_ep); 71962306a36Sopenharmony_ci if (!req) 72062306a36Sopenharmony_ci break; 72162306a36Sopenharmony_ci len = snd_ump_transmit(ep->ump, (u32 *)req->buf, 72262306a36Sopenharmony_ci midi2->info.req_buf_size); 72362306a36Sopenharmony_ci if (len <= 0) { 72462306a36Sopenharmony_ci put_empty_request(req); 72562306a36Sopenharmony_ci break; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci req->length = len; 72962306a36Sopenharmony_ci if (queue_request_ep_in(req) < 0) 73062306a36Sopenharmony_ci break; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci/* Complete handler for UMP EP-in requests */ 73562306a36Sopenharmony_cistatic void f_midi2_ep_in_complete(struct usb_ep *usb_ep, 73662306a36Sopenharmony_ci struct usb_request *req) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct f_midi2_req_ctx *ctx = req->context; 73962306a36Sopenharmony_ci struct f_midi2_ep *ep = ctx->usb_ep->ep; 74062306a36Sopenharmony_ci struct f_midi2 *midi2 = ep->card; 74162306a36Sopenharmony_ci int status = req->status; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci put_empty_request(req); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if (status) { 74662306a36Sopenharmony_ci DBG(midi2, "%s complete error %d: %d/%d\n", 74762306a36Sopenharmony_ci usb_ep->name, status, req->actual, req->length); 74862306a36Sopenharmony_ci return; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci process_ump_transmit(ep); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci/* 75562306a36Sopenharmony_ci * MIDI1 (altset 0) USB request handling 75662306a36Sopenharmony_ci */ 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci/* process one MIDI byte -- copied from f_midi.c 75962306a36Sopenharmony_ci * 76062306a36Sopenharmony_ci * fill the packet or request if needed 76162306a36Sopenharmony_ci * returns true if the request became empty (queued) 76262306a36Sopenharmony_ci */ 76362306a36Sopenharmony_cistatic bool process_midi1_byte(struct f_midi2 *midi2, u8 cable, u8 b, 76462306a36Sopenharmony_ci struct usb_request **req_p) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci struct f_midi2_midi1_port *port = &midi2->midi1_port[cable]; 76762306a36Sopenharmony_ci u8 p[4] = { cable << 4, 0, 0, 0 }; 76862306a36Sopenharmony_ci int next_state = STATE_INITIAL; 76962306a36Sopenharmony_ci struct usb_request *req = *req_p; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci switch (b) { 77262306a36Sopenharmony_ci case 0xf8 ... 0xff: 77362306a36Sopenharmony_ci /* System Real-Time Messages */ 77462306a36Sopenharmony_ci p[0] |= 0x0f; 77562306a36Sopenharmony_ci p[1] = b; 77662306a36Sopenharmony_ci next_state = port->state; 77762306a36Sopenharmony_ci port->state = STATE_REAL_TIME; 77862306a36Sopenharmony_ci break; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci case 0xf7: 78162306a36Sopenharmony_ci /* End of SysEx */ 78262306a36Sopenharmony_ci switch (port->state) { 78362306a36Sopenharmony_ci case STATE_SYSEX_0: 78462306a36Sopenharmony_ci p[0] |= 0x05; 78562306a36Sopenharmony_ci p[1] = 0xf7; 78662306a36Sopenharmony_ci next_state = STATE_FINISHED; 78762306a36Sopenharmony_ci break; 78862306a36Sopenharmony_ci case STATE_SYSEX_1: 78962306a36Sopenharmony_ci p[0] |= 0x06; 79062306a36Sopenharmony_ci p[1] = port->data[0]; 79162306a36Sopenharmony_ci p[2] = 0xf7; 79262306a36Sopenharmony_ci next_state = STATE_FINISHED; 79362306a36Sopenharmony_ci break; 79462306a36Sopenharmony_ci case STATE_SYSEX_2: 79562306a36Sopenharmony_ci p[0] |= 0x07; 79662306a36Sopenharmony_ci p[1] = port->data[0]; 79762306a36Sopenharmony_ci p[2] = port->data[1]; 79862306a36Sopenharmony_ci p[3] = 0xf7; 79962306a36Sopenharmony_ci next_state = STATE_FINISHED; 80062306a36Sopenharmony_ci break; 80162306a36Sopenharmony_ci default: 80262306a36Sopenharmony_ci /* Ignore byte */ 80362306a36Sopenharmony_ci next_state = port->state; 80462306a36Sopenharmony_ci port->state = STATE_INITIAL; 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci break; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci case 0xf0 ... 0xf6: 80962306a36Sopenharmony_ci /* System Common Messages */ 81062306a36Sopenharmony_ci port->data[0] = port->data[1] = 0; 81162306a36Sopenharmony_ci port->state = STATE_INITIAL; 81262306a36Sopenharmony_ci switch (b) { 81362306a36Sopenharmony_ci case 0xf0: 81462306a36Sopenharmony_ci port->data[0] = b; 81562306a36Sopenharmony_ci port->data[1] = 0; 81662306a36Sopenharmony_ci next_state = STATE_SYSEX_1; 81762306a36Sopenharmony_ci break; 81862306a36Sopenharmony_ci case 0xf1: 81962306a36Sopenharmony_ci case 0xf3: 82062306a36Sopenharmony_ci port->data[0] = b; 82162306a36Sopenharmony_ci next_state = STATE_1PARAM; 82262306a36Sopenharmony_ci break; 82362306a36Sopenharmony_ci case 0xf2: 82462306a36Sopenharmony_ci port->data[0] = b; 82562306a36Sopenharmony_ci next_state = STATE_2PARAM_1; 82662306a36Sopenharmony_ci break; 82762306a36Sopenharmony_ci case 0xf4: 82862306a36Sopenharmony_ci case 0xf5: 82962306a36Sopenharmony_ci next_state = STATE_INITIAL; 83062306a36Sopenharmony_ci break; 83162306a36Sopenharmony_ci case 0xf6: 83262306a36Sopenharmony_ci p[0] |= 0x05; 83362306a36Sopenharmony_ci p[1] = 0xf6; 83462306a36Sopenharmony_ci next_state = STATE_FINISHED; 83562306a36Sopenharmony_ci break; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci break; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci case 0x80 ... 0xef: 84062306a36Sopenharmony_ci /* 84162306a36Sopenharmony_ci * Channel Voice Messages, Channel Mode Messages 84262306a36Sopenharmony_ci * and Control Change Messages. 84362306a36Sopenharmony_ci */ 84462306a36Sopenharmony_ci port->data[0] = b; 84562306a36Sopenharmony_ci port->data[1] = 0; 84662306a36Sopenharmony_ci port->state = STATE_INITIAL; 84762306a36Sopenharmony_ci if (b >= 0xc0 && b <= 0xdf) 84862306a36Sopenharmony_ci next_state = STATE_1PARAM; 84962306a36Sopenharmony_ci else 85062306a36Sopenharmony_ci next_state = STATE_2PARAM_1; 85162306a36Sopenharmony_ci break; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci case 0x00 ... 0x7f: 85462306a36Sopenharmony_ci /* Message parameters */ 85562306a36Sopenharmony_ci switch (port->state) { 85662306a36Sopenharmony_ci case STATE_1PARAM: 85762306a36Sopenharmony_ci if (port->data[0] < 0xf0) 85862306a36Sopenharmony_ci p[0] |= port->data[0] >> 4; 85962306a36Sopenharmony_ci else 86062306a36Sopenharmony_ci p[0] |= 0x02; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci p[1] = port->data[0]; 86362306a36Sopenharmony_ci p[2] = b; 86462306a36Sopenharmony_ci /* This is to allow Running State Messages */ 86562306a36Sopenharmony_ci next_state = STATE_1PARAM; 86662306a36Sopenharmony_ci break; 86762306a36Sopenharmony_ci case STATE_2PARAM_1: 86862306a36Sopenharmony_ci port->data[1] = b; 86962306a36Sopenharmony_ci next_state = STATE_2PARAM_2; 87062306a36Sopenharmony_ci break; 87162306a36Sopenharmony_ci case STATE_2PARAM_2: 87262306a36Sopenharmony_ci if (port->data[0] < 0xf0) 87362306a36Sopenharmony_ci p[0] |= port->data[0] >> 4; 87462306a36Sopenharmony_ci else 87562306a36Sopenharmony_ci p[0] |= 0x03; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci p[1] = port->data[0]; 87862306a36Sopenharmony_ci p[2] = port->data[1]; 87962306a36Sopenharmony_ci p[3] = b; 88062306a36Sopenharmony_ci /* This is to allow Running State Messages */ 88162306a36Sopenharmony_ci next_state = STATE_2PARAM_1; 88262306a36Sopenharmony_ci break; 88362306a36Sopenharmony_ci case STATE_SYSEX_0: 88462306a36Sopenharmony_ci port->data[0] = b; 88562306a36Sopenharmony_ci next_state = STATE_SYSEX_1; 88662306a36Sopenharmony_ci break; 88762306a36Sopenharmony_ci case STATE_SYSEX_1: 88862306a36Sopenharmony_ci port->data[1] = b; 88962306a36Sopenharmony_ci next_state = STATE_SYSEX_2; 89062306a36Sopenharmony_ci break; 89162306a36Sopenharmony_ci case STATE_SYSEX_2: 89262306a36Sopenharmony_ci p[0] |= 0x04; 89362306a36Sopenharmony_ci p[1] = port->data[0]; 89462306a36Sopenharmony_ci p[2] = port->data[1]; 89562306a36Sopenharmony_ci p[3] = b; 89662306a36Sopenharmony_ci next_state = STATE_SYSEX_0; 89762306a36Sopenharmony_ci break; 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci break; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci /* States where we have to write into the USB request */ 90362306a36Sopenharmony_ci if (next_state == STATE_FINISHED || 90462306a36Sopenharmony_ci port->state == STATE_SYSEX_2 || 90562306a36Sopenharmony_ci port->state == STATE_1PARAM || 90662306a36Sopenharmony_ci port->state == STATE_2PARAM_2 || 90762306a36Sopenharmony_ci port->state == STATE_REAL_TIME) { 90862306a36Sopenharmony_ci memcpy(req->buf + req->length, p, sizeof(p)); 90962306a36Sopenharmony_ci req->length += sizeof(p); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (next_state == STATE_FINISHED) { 91262306a36Sopenharmony_ci next_state = STATE_INITIAL; 91362306a36Sopenharmony_ci port->data[0] = port->data[1] = 0; 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci if (midi2->info.req_buf_size - req->length <= 4) { 91762306a36Sopenharmony_ci queue_request_ep_raw(req); 91862306a36Sopenharmony_ci *req_p = NULL; 91962306a36Sopenharmony_ci return true; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci port->state = next_state; 92462306a36Sopenharmony_ci return false; 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci/* process all pending MIDI bytes in the internal buffer; 92862306a36Sopenharmony_ci * returns true if the request gets empty 92962306a36Sopenharmony_ci * returns false if all have been processed 93062306a36Sopenharmony_ci */ 93162306a36Sopenharmony_cistatic bool process_midi1_pending_buf(struct f_midi2 *midi2, 93262306a36Sopenharmony_ci struct usb_request **req_p) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci unsigned int cable, c; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci for (cable = 0; cable < midi2->num_midi1_in; cable++) { 93762306a36Sopenharmony_ci struct f_midi2_midi1_port *port = &midi2->midi1_port[cable]; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci if (!port->pending) 94062306a36Sopenharmony_ci continue; 94162306a36Sopenharmony_ci for (c = 0; c < port->pending; c++) { 94262306a36Sopenharmony_ci if (process_midi1_byte(midi2, cable, port->buf[c], 94362306a36Sopenharmony_ci req_p)) { 94462306a36Sopenharmony_ci port->pending -= c; 94562306a36Sopenharmony_ci if (port->pending) 94662306a36Sopenharmony_ci memmove(port->buf, port->buf + c, 94762306a36Sopenharmony_ci port->pending); 94862306a36Sopenharmony_ci return true; 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci port->pending = 0; 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci return false; 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci/* fill the MIDI bytes onto the temporary buffer 95862306a36Sopenharmony_ci */ 95962306a36Sopenharmony_cistatic void fill_midi1_pending_buf(struct f_midi2 *midi2, u8 cable, u8 *buf, 96062306a36Sopenharmony_ci unsigned int size) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct f_midi2_midi1_port *port = &midi2->midi1_port[cable]; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci if (port->pending + size > sizeof(port->buf)) 96562306a36Sopenharmony_ci return; 96662306a36Sopenharmony_ci memcpy(port->buf + port->pending, buf, size); 96762306a36Sopenharmony_ci port->pending += size; 96862306a36Sopenharmony_ci} 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci/* try to process data given from the associated UMP stream */ 97162306a36Sopenharmony_cistatic void process_midi1_transmit(struct f_midi2 *midi2) 97262306a36Sopenharmony_ci{ 97362306a36Sopenharmony_ci struct f_midi2_usb_ep *usb_ep = &midi2->midi1_ep_in; 97462306a36Sopenharmony_ci struct f_midi2_ep *ep = &midi2->midi2_eps[0]; 97562306a36Sopenharmony_ci struct usb_request *req = NULL; 97662306a36Sopenharmony_ci /* 12 is the largest outcome (4 MIDI1 cmds) for a single UMP packet */ 97762306a36Sopenharmony_ci unsigned char outbuf[12]; 97862306a36Sopenharmony_ci unsigned char group, cable; 97962306a36Sopenharmony_ci int len, size; 98062306a36Sopenharmony_ci u32 ump; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (!usb_ep->usb_ep || !usb_ep->usb_ep->enabled) 98362306a36Sopenharmony_ci return; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci for (;;) { 98662306a36Sopenharmony_ci if (!req) { 98762306a36Sopenharmony_ci req = get_empty_request(usb_ep); 98862306a36Sopenharmony_ci if (!req) 98962306a36Sopenharmony_ci break; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (process_midi1_pending_buf(midi2, &req)) 99362306a36Sopenharmony_ci continue; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci len = snd_ump_transmit(ep->ump, &ump, 4); 99662306a36Sopenharmony_ci if (len <= 0) 99762306a36Sopenharmony_ci break; 99862306a36Sopenharmony_ci if (snd_ump_receive_ump_val(ep->ump, ump) <= 0) 99962306a36Sopenharmony_ci continue; 100062306a36Sopenharmony_ci size = snd_ump_convert_from_ump(ep->ump->input_buf, outbuf, 100162306a36Sopenharmony_ci &group); 100262306a36Sopenharmony_ci if (size <= 0) 100362306a36Sopenharmony_ci continue; 100462306a36Sopenharmony_ci cable = ep->in_group_to_cable[group]; 100562306a36Sopenharmony_ci if (!cable) 100662306a36Sopenharmony_ci continue; 100762306a36Sopenharmony_ci cable--; /* to 0-base */ 100862306a36Sopenharmony_ci fill_midi1_pending_buf(midi2, cable, outbuf, size); 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci if (req) { 101262306a36Sopenharmony_ci if (req->length) 101362306a36Sopenharmony_ci queue_request_ep_raw(req); 101462306a36Sopenharmony_ci else 101562306a36Sopenharmony_ci put_empty_request(req); 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci} 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci/* complete handler for MIDI1 EP-in requests */ 102062306a36Sopenharmony_cistatic void f_midi2_midi1_ep_in_complete(struct usb_ep *usb_ep, 102162306a36Sopenharmony_ci struct usb_request *req) 102262306a36Sopenharmony_ci{ 102362306a36Sopenharmony_ci struct f_midi2_req_ctx *ctx = req->context; 102462306a36Sopenharmony_ci struct f_midi2 *midi2 = ctx->usb_ep->card; 102562306a36Sopenharmony_ci int status = req->status; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci put_empty_request(req); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci if (status) { 103062306a36Sopenharmony_ci DBG(midi2, "%s complete error %d: %d/%d\n", 103162306a36Sopenharmony_ci usb_ep->name, status, req->actual, req->length); 103262306a36Sopenharmony_ci return; 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci process_midi1_transmit(midi2); 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci/* complete handler for MIDI1 EP-out requests */ 103962306a36Sopenharmony_cistatic void f_midi2_midi1_ep_out_complete(struct usb_ep *usb_ep, 104062306a36Sopenharmony_ci struct usb_request *req) 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci struct f_midi2_req_ctx *ctx = req->context; 104362306a36Sopenharmony_ci struct f_midi2 *midi2 = ctx->usb_ep->card; 104462306a36Sopenharmony_ci struct f_midi2_ep *ep; 104562306a36Sopenharmony_ci struct ump_cvt_to_ump *cvt = &midi2->midi1_ump_cvt; 104662306a36Sopenharmony_ci static const u8 midi1_packet_bytes[16] = { 104762306a36Sopenharmony_ci 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 104862306a36Sopenharmony_ci }; 104962306a36Sopenharmony_ci unsigned int group, cable, bytes, c, len; 105062306a36Sopenharmony_ci int status = req->status; 105162306a36Sopenharmony_ci const u8 *buf = req->buf; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci if (status) { 105462306a36Sopenharmony_ci DBG(midi2, "%s complete error %d: %d/%d\n", 105562306a36Sopenharmony_ci usb_ep->name, status, req->actual, req->length); 105662306a36Sopenharmony_ci goto error; 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci len = req->actual >> 2; 106062306a36Sopenharmony_ci for (; len; len--, buf += 4) { 106162306a36Sopenharmony_ci cable = *buf >> 4; 106262306a36Sopenharmony_ci ep = midi2->out_cable_mapping[cable].ep; 106362306a36Sopenharmony_ci if (!ep) 106462306a36Sopenharmony_ci continue; 106562306a36Sopenharmony_ci group = midi2->out_cable_mapping[cable].group; 106662306a36Sopenharmony_ci bytes = midi1_packet_bytes[*buf & 0x0f]; 106762306a36Sopenharmony_ci for (c = 0; c < bytes; c++) { 106862306a36Sopenharmony_ci snd_ump_convert_to_ump(cvt, group, ep->info.protocol, 106962306a36Sopenharmony_ci buf[c + 1]); 107062306a36Sopenharmony_ci if (cvt->ump_bytes) { 107162306a36Sopenharmony_ci snd_ump_receive(ep->ump, cvt->ump, 107262306a36Sopenharmony_ci cvt->ump_bytes); 107362306a36Sopenharmony_ci cvt->ump_bytes = 0; 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci if (midi2->operation_mode != MIDI_OP_MODE_MIDI1) 107962306a36Sopenharmony_ci goto error; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (queue_request_ep_raw(req)) 108262306a36Sopenharmony_ci goto error; 108362306a36Sopenharmony_ci return; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci error: 108662306a36Sopenharmony_ci put_empty_request(req); 108762306a36Sopenharmony_ci} 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci/* 109062306a36Sopenharmony_ci * Common EP handling helpers 109162306a36Sopenharmony_ci */ 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci/* Start MIDI EP */ 109462306a36Sopenharmony_cistatic int f_midi2_start_ep(struct f_midi2_usb_ep *usb_ep, 109562306a36Sopenharmony_ci struct usb_function *fn) 109662306a36Sopenharmony_ci{ 109762306a36Sopenharmony_ci int err; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci if (!usb_ep->usb_ep) 110062306a36Sopenharmony_ci return 0; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci usb_ep_disable(usb_ep->usb_ep); 110362306a36Sopenharmony_ci err = config_ep_by_speed(usb_ep->card->gadget, fn, usb_ep->usb_ep); 110462306a36Sopenharmony_ci if (err) 110562306a36Sopenharmony_ci return err; 110662306a36Sopenharmony_ci return usb_ep_enable(usb_ep->usb_ep); 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci/* Drop pending requests */ 111062306a36Sopenharmony_cistatic void f_midi2_drop_reqs(struct f_midi2_usb_ep *usb_ep) 111162306a36Sopenharmony_ci{ 111262306a36Sopenharmony_ci int i; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci if (!usb_ep->usb_ep || !usb_ep->num_reqs) 111562306a36Sopenharmony_ci return; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci for (i = 0; i < usb_ep->num_reqs; i++) { 111862306a36Sopenharmony_ci if (!test_bit(i, &usb_ep->free_reqs) && usb_ep->reqs[i].req) { 111962306a36Sopenharmony_ci usb_ep_dequeue(usb_ep->usb_ep, usb_ep->reqs[i].req); 112062306a36Sopenharmony_ci set_bit(i, &usb_ep->free_reqs); 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci} 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci/* Allocate requests for the given EP */ 112662306a36Sopenharmony_cistatic int f_midi2_alloc_ep_reqs(struct f_midi2_usb_ep *usb_ep) 112762306a36Sopenharmony_ci{ 112862306a36Sopenharmony_ci struct f_midi2 *midi2 = usb_ep->card; 112962306a36Sopenharmony_ci int i; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci if (!usb_ep->usb_ep) 113262306a36Sopenharmony_ci return 0; 113362306a36Sopenharmony_ci if (!usb_ep->reqs) 113462306a36Sopenharmony_ci return -EINVAL; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci for (i = 0; i < midi2->info.num_reqs; i++) { 113762306a36Sopenharmony_ci if (usb_ep->reqs[i].req) 113862306a36Sopenharmony_ci continue; 113962306a36Sopenharmony_ci usb_ep->reqs[i].req = alloc_ep_req(usb_ep->usb_ep, 114062306a36Sopenharmony_ci midi2->info.req_buf_size); 114162306a36Sopenharmony_ci if (!usb_ep->reqs[i].req) 114262306a36Sopenharmony_ci return -ENOMEM; 114362306a36Sopenharmony_ci usb_ep->reqs[i].req->context = &usb_ep->reqs[i]; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci return 0; 114662306a36Sopenharmony_ci} 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci/* Free allocated requests */ 114962306a36Sopenharmony_cistatic void f_midi2_free_ep_reqs(struct f_midi2_usb_ep *usb_ep) 115062306a36Sopenharmony_ci{ 115162306a36Sopenharmony_ci struct f_midi2 *midi2 = usb_ep->card; 115262306a36Sopenharmony_ci int i; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci for (i = 0; i < midi2->info.num_reqs; i++) { 115562306a36Sopenharmony_ci if (!usb_ep->reqs[i].req) 115662306a36Sopenharmony_ci continue; 115762306a36Sopenharmony_ci free_ep_req(usb_ep->usb_ep, usb_ep->reqs[i].req); 115862306a36Sopenharmony_ci usb_ep->reqs[i].req = NULL; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci} 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci/* Initialize EP */ 116362306a36Sopenharmony_cistatic int f_midi2_init_ep(struct f_midi2 *midi2, struct f_midi2_ep *ep, 116462306a36Sopenharmony_ci struct f_midi2_usb_ep *usb_ep, 116562306a36Sopenharmony_ci void *desc, 116662306a36Sopenharmony_ci void (*complete)(struct usb_ep *usb_ep, 116762306a36Sopenharmony_ci struct usb_request *req)) 116862306a36Sopenharmony_ci{ 116962306a36Sopenharmony_ci int i; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci usb_ep->card = midi2; 117262306a36Sopenharmony_ci usb_ep->ep = ep; 117362306a36Sopenharmony_ci usb_ep->usb_ep = usb_ep_autoconfig(midi2->gadget, desc); 117462306a36Sopenharmony_ci if (!usb_ep->usb_ep) 117562306a36Sopenharmony_ci return -ENODEV; 117662306a36Sopenharmony_ci usb_ep->complete = complete; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci usb_ep->reqs = kcalloc(midi2->info.num_reqs, sizeof(*usb_ep->reqs), 117962306a36Sopenharmony_ci GFP_KERNEL); 118062306a36Sopenharmony_ci if (!usb_ep->reqs) 118162306a36Sopenharmony_ci return -ENOMEM; 118262306a36Sopenharmony_ci for (i = 0; i < midi2->info.num_reqs; i++) { 118362306a36Sopenharmony_ci usb_ep->reqs[i].index = i; 118462306a36Sopenharmony_ci usb_ep->reqs[i].usb_ep = usb_ep; 118562306a36Sopenharmony_ci set_bit(i, &usb_ep->free_reqs); 118662306a36Sopenharmony_ci usb_ep->num_reqs++; 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci return 0; 119062306a36Sopenharmony_ci} 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci/* Free EP */ 119362306a36Sopenharmony_cistatic void f_midi2_free_ep(struct f_midi2_usb_ep *usb_ep) 119462306a36Sopenharmony_ci{ 119562306a36Sopenharmony_ci f_midi2_drop_reqs(usb_ep); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci f_midi2_free_ep_reqs(usb_ep); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci kfree(usb_ep->reqs); 120062306a36Sopenharmony_ci usb_ep->num_reqs = 0; 120162306a36Sopenharmony_ci usb_ep->free_reqs = 0; 120262306a36Sopenharmony_ci usb_ep->reqs = NULL; 120362306a36Sopenharmony_ci} 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci/* Queue requests for EP-out at start */ 120662306a36Sopenharmony_cistatic void f_midi2_queue_out_reqs(struct f_midi2_usb_ep *usb_ep) 120762306a36Sopenharmony_ci{ 120862306a36Sopenharmony_ci int i, err; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci if (!usb_ep->usb_ep) 121162306a36Sopenharmony_ci return; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci for (i = 0; i < usb_ep->num_reqs; i++) { 121462306a36Sopenharmony_ci if (!test_bit(i, &usb_ep->free_reqs) || !usb_ep->reqs[i].req) 121562306a36Sopenharmony_ci continue; 121662306a36Sopenharmony_ci usb_ep->reqs[i].req->complete = usb_ep->complete; 121762306a36Sopenharmony_ci err = usb_ep_queue(usb_ep->usb_ep, usb_ep->reqs[i].req, 121862306a36Sopenharmony_ci GFP_ATOMIC); 121962306a36Sopenharmony_ci if (!err) 122062306a36Sopenharmony_ci clear_bit(i, &usb_ep->free_reqs); 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci} 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci/* 122562306a36Sopenharmony_ci * Gadget Function callbacks 122662306a36Sopenharmony_ci */ 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci/* stop both IN and OUT EPs */ 122962306a36Sopenharmony_cistatic void f_midi2_stop_eps(struct f_midi2_usb_ep *ep_in, 123062306a36Sopenharmony_ci struct f_midi2_usb_ep *ep_out) 123162306a36Sopenharmony_ci{ 123262306a36Sopenharmony_ci f_midi2_drop_reqs(ep_in); 123362306a36Sopenharmony_ci f_midi2_drop_reqs(ep_out); 123462306a36Sopenharmony_ci f_midi2_free_ep_reqs(ep_in); 123562306a36Sopenharmony_ci f_midi2_free_ep_reqs(ep_out); 123662306a36Sopenharmony_ci} 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci/* start/queue both IN and OUT EPs */ 123962306a36Sopenharmony_cistatic int f_midi2_start_eps(struct f_midi2_usb_ep *ep_in, 124062306a36Sopenharmony_ci struct f_midi2_usb_ep *ep_out, 124162306a36Sopenharmony_ci struct usb_function *fn) 124262306a36Sopenharmony_ci{ 124362306a36Sopenharmony_ci int err; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci err = f_midi2_start_ep(ep_in, fn); 124662306a36Sopenharmony_ci if (err) 124762306a36Sopenharmony_ci return err; 124862306a36Sopenharmony_ci err = f_midi2_start_ep(ep_out, fn); 124962306a36Sopenharmony_ci if (err) 125062306a36Sopenharmony_ci return err; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci err = f_midi2_alloc_ep_reqs(ep_in); 125362306a36Sopenharmony_ci if (err) 125462306a36Sopenharmony_ci return err; 125562306a36Sopenharmony_ci err = f_midi2_alloc_ep_reqs(ep_out); 125662306a36Sopenharmony_ci if (err) 125762306a36Sopenharmony_ci return err; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci f_midi2_queue_out_reqs(ep_out); 126062306a36Sopenharmony_ci return 0; 126162306a36Sopenharmony_ci} 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci/* gadget function set_alt callback */ 126462306a36Sopenharmony_cistatic int f_midi2_set_alt(struct usb_function *fn, unsigned int intf, 126562306a36Sopenharmony_ci unsigned int alt) 126662306a36Sopenharmony_ci{ 126762306a36Sopenharmony_ci struct f_midi2 *midi2 = func_to_midi2(fn); 126862306a36Sopenharmony_ci struct f_midi2_ep *ep; 126962306a36Sopenharmony_ci int i, op_mode, err; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci if (intf != midi2->midi_if || alt > 1) 127262306a36Sopenharmony_ci return 0; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci if (alt == 0) 127562306a36Sopenharmony_ci op_mode = MIDI_OP_MODE_MIDI1; 127662306a36Sopenharmony_ci else if (alt == 1) 127762306a36Sopenharmony_ci op_mode = MIDI_OP_MODE_MIDI2; 127862306a36Sopenharmony_ci else 127962306a36Sopenharmony_ci op_mode = MIDI_OP_MODE_UNSET; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci if (midi2->operation_mode == op_mode) 128262306a36Sopenharmony_ci return 0; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci midi2->operation_mode = op_mode; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci if (op_mode != MIDI_OP_MODE_MIDI1) 128762306a36Sopenharmony_ci f_midi2_stop_eps(&midi2->midi1_ep_in, &midi2->midi1_ep_out); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (op_mode != MIDI_OP_MODE_MIDI2) { 129062306a36Sopenharmony_ci for (i = 0; i < midi2->num_eps; i++) { 129162306a36Sopenharmony_ci ep = &midi2->midi2_eps[i]; 129262306a36Sopenharmony_ci f_midi2_stop_eps(&ep->ep_in, &ep->ep_out); 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci } 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci if (op_mode == MIDI_OP_MODE_MIDI1) 129762306a36Sopenharmony_ci return f_midi2_start_eps(&midi2->midi1_ep_in, 129862306a36Sopenharmony_ci &midi2->midi1_ep_out, fn); 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci if (op_mode == MIDI_OP_MODE_MIDI2) { 130162306a36Sopenharmony_ci for (i = 0; i < midi2->num_eps; i++) { 130262306a36Sopenharmony_ci ep = &midi2->midi2_eps[i]; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci err = f_midi2_start_eps(&ep->ep_in, &ep->ep_out, fn); 130562306a36Sopenharmony_ci if (err) 130662306a36Sopenharmony_ci return err; 130762306a36Sopenharmony_ci } 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci return 0; 131162306a36Sopenharmony_ci} 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci/* gadget function get_alt callback */ 131462306a36Sopenharmony_cistatic int f_midi2_get_alt(struct usb_function *fn, unsigned int intf) 131562306a36Sopenharmony_ci{ 131662306a36Sopenharmony_ci struct f_midi2 *midi2 = func_to_midi2(fn); 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci if (intf == midi2->midi_if && 131962306a36Sopenharmony_ci midi2->operation_mode == MIDI_OP_MODE_MIDI2) 132062306a36Sopenharmony_ci return 1; 132162306a36Sopenharmony_ci return 0; 132262306a36Sopenharmony_ci} 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci/* convert UMP direction to USB MIDI 2.0 direction */ 132562306a36Sopenharmony_cistatic unsigned int ump_to_usb_dir(unsigned int ump_dir) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci switch (ump_dir) { 132862306a36Sopenharmony_ci case SNDRV_UMP_DIR_INPUT: 132962306a36Sopenharmony_ci return USB_MS_GR_TRM_BLOCK_TYPE_INPUT_ONLY; 133062306a36Sopenharmony_ci case SNDRV_UMP_DIR_OUTPUT: 133162306a36Sopenharmony_ci return USB_MS_GR_TRM_BLOCK_TYPE_OUTPUT_ONLY; 133262306a36Sopenharmony_ci default: 133362306a36Sopenharmony_ci return USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL; 133462306a36Sopenharmony_ci } 133562306a36Sopenharmony_ci} 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci/* assign GTB descriptors (for the given request) */ 133862306a36Sopenharmony_cistatic void assign_block_descriptors(struct f_midi2 *midi2, 133962306a36Sopenharmony_ci struct usb_request *req, 134062306a36Sopenharmony_ci int max_len) 134162306a36Sopenharmony_ci{ 134262306a36Sopenharmony_ci struct usb_ms20_gr_trm_block_header_descriptor header; 134362306a36Sopenharmony_ci struct usb_ms20_gr_trm_block_descriptor *desc; 134462306a36Sopenharmony_ci struct f_midi2_block_info *b; 134562306a36Sopenharmony_ci struct f_midi2_ep *ep; 134662306a36Sopenharmony_ci int i, blk, len; 134762306a36Sopenharmony_ci char *data; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci len = sizeof(gtb_header_desc) + sizeof(gtb_desc) * midi2->total_blocks; 135062306a36Sopenharmony_ci if (WARN_ON(len > midi2->info.req_buf_size)) 135162306a36Sopenharmony_ci return; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci header = gtb_header_desc; 135462306a36Sopenharmony_ci header.wTotalLength = cpu_to_le16(len); 135562306a36Sopenharmony_ci if (max_len < len) { 135662306a36Sopenharmony_ci len = min_t(int, len, sizeof(header)); 135762306a36Sopenharmony_ci memcpy(req->buf, &header, len); 135862306a36Sopenharmony_ci req->length = len; 135962306a36Sopenharmony_ci req->zero = len < max_len; 136062306a36Sopenharmony_ci return; 136162306a36Sopenharmony_ci } 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci memcpy(req->buf, &header, sizeof(header)); 136462306a36Sopenharmony_ci data = req->buf + sizeof(header); 136562306a36Sopenharmony_ci for (i = 0; i < midi2->num_eps; i++) { 136662306a36Sopenharmony_ci ep = &midi2->midi2_eps[i]; 136762306a36Sopenharmony_ci for (blk = 0; blk < ep->num_blks; blk++) { 136862306a36Sopenharmony_ci b = &ep->blks[blk].info; 136962306a36Sopenharmony_ci desc = (struct usb_ms20_gr_trm_block_descriptor *)data; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci *desc = gtb_desc; 137262306a36Sopenharmony_ci desc->bGrpTrmBlkID = ep->blks[blk].gtb_id; 137362306a36Sopenharmony_ci desc->bGrpTrmBlkType = ump_to_usb_dir(b->direction); 137462306a36Sopenharmony_ci desc->nGroupTrm = b->first_group; 137562306a36Sopenharmony_ci desc->nNumGroupTrm = b->num_groups; 137662306a36Sopenharmony_ci desc->iBlockItem = ep->blks[blk].string_id; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci if (ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2) 137962306a36Sopenharmony_ci desc->bMIDIProtocol = USB_MS_MIDI_PROTO_2_0; 138062306a36Sopenharmony_ci else 138162306a36Sopenharmony_ci desc->bMIDIProtocol = USB_MS_MIDI_PROTO_1_0_128; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci if (b->is_midi1 == 2) { 138462306a36Sopenharmony_ci desc->wMaxInputBandwidth = cpu_to_le16(1); 138562306a36Sopenharmony_ci desc->wMaxOutputBandwidth = cpu_to_le16(1); 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci data += sizeof(*desc); 138962306a36Sopenharmony_ci } 139062306a36Sopenharmony_ci } 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci req->length = len; 139362306a36Sopenharmony_ci req->zero = len < max_len; 139462306a36Sopenharmony_ci} 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci/* gadget function setup callback: handle GTB requests */ 139762306a36Sopenharmony_cistatic int f_midi2_setup(struct usb_function *fn, 139862306a36Sopenharmony_ci const struct usb_ctrlrequest *ctrl) 139962306a36Sopenharmony_ci{ 140062306a36Sopenharmony_ci struct f_midi2 *midi2 = func_to_midi2(fn); 140162306a36Sopenharmony_ci struct usb_composite_dev *cdev = fn->config->cdev; 140262306a36Sopenharmony_ci struct usb_request *req = cdev->req; 140362306a36Sopenharmony_ci u16 value, length; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD || 140662306a36Sopenharmony_ci ctrl->bRequest != USB_REQ_GET_DESCRIPTOR) 140762306a36Sopenharmony_ci return -EOPNOTSUPP; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci value = le16_to_cpu(ctrl->wValue); 141062306a36Sopenharmony_ci length = le16_to_cpu(ctrl->wLength); 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci if ((value >> 8) != USB_DT_CS_GR_TRM_BLOCK) 141362306a36Sopenharmony_ci return -EOPNOTSUPP; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci /* handle only altset 1 */ 141662306a36Sopenharmony_ci if ((value & 0xff) != 1) 141762306a36Sopenharmony_ci return -EOPNOTSUPP; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci assign_block_descriptors(midi2, req, length); 142062306a36Sopenharmony_ci return usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); 142162306a36Sopenharmony_ci} 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci/* gadget function disable callback */ 142462306a36Sopenharmony_cistatic void f_midi2_disable(struct usb_function *fn) 142562306a36Sopenharmony_ci{ 142662306a36Sopenharmony_ci struct f_midi2 *midi2 = func_to_midi2(fn); 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci midi2->operation_mode = MIDI_OP_MODE_UNSET; 142962306a36Sopenharmony_ci} 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci/* 143262306a36Sopenharmony_ci * ALSA UMP ops: most of them are NOPs, only trigger for write is needed 143362306a36Sopenharmony_ci */ 143462306a36Sopenharmony_cistatic int f_midi2_ump_open(struct snd_ump_endpoint *ump, int dir) 143562306a36Sopenharmony_ci{ 143662306a36Sopenharmony_ci return 0; 143762306a36Sopenharmony_ci} 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_cistatic void f_midi2_ump_close(struct snd_ump_endpoint *ump, int dir) 144062306a36Sopenharmony_ci{ 144162306a36Sopenharmony_ci} 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_cistatic void f_midi2_ump_trigger(struct snd_ump_endpoint *ump, int dir, int up) 144462306a36Sopenharmony_ci{ 144562306a36Sopenharmony_ci struct f_midi2_ep *ep = ump->private_data; 144662306a36Sopenharmony_ci struct f_midi2 *midi2 = ep->card; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci if (up && dir == SNDRV_RAWMIDI_STREAM_OUTPUT) { 144962306a36Sopenharmony_ci switch (midi2->operation_mode) { 145062306a36Sopenharmony_ci case MIDI_OP_MODE_MIDI1: 145162306a36Sopenharmony_ci process_midi1_transmit(midi2); 145262306a36Sopenharmony_ci break; 145362306a36Sopenharmony_ci case MIDI_OP_MODE_MIDI2: 145462306a36Sopenharmony_ci process_ump_transmit(ep); 145562306a36Sopenharmony_ci break; 145662306a36Sopenharmony_ci } 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci} 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_cistatic void f_midi2_ump_drain(struct snd_ump_endpoint *ump, int dir) 146162306a36Sopenharmony_ci{ 146262306a36Sopenharmony_ci} 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_cistatic const struct snd_ump_ops f_midi2_ump_ops = { 146562306a36Sopenharmony_ci .open = f_midi2_ump_open, 146662306a36Sopenharmony_ci .close = f_midi2_ump_close, 146762306a36Sopenharmony_ci .trigger = f_midi2_ump_trigger, 146862306a36Sopenharmony_ci .drain = f_midi2_ump_drain, 146962306a36Sopenharmony_ci}; 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci/* 147262306a36Sopenharmony_ci * "Operation Mode" control element 147362306a36Sopenharmony_ci */ 147462306a36Sopenharmony_cistatic int f_midi2_operation_mode_info(struct snd_kcontrol *kcontrol, 147562306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 147662306a36Sopenharmony_ci{ 147762306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 147862306a36Sopenharmony_ci uinfo->count = 1; 147962306a36Sopenharmony_ci uinfo->value.integer.min = MIDI_OP_MODE_UNSET; 148062306a36Sopenharmony_ci uinfo->value.integer.max = MIDI_OP_MODE_MIDI2; 148162306a36Sopenharmony_ci return 0; 148262306a36Sopenharmony_ci} 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_cistatic int f_midi2_operation_mode_get(struct snd_kcontrol *kcontrol, 148562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 148662306a36Sopenharmony_ci{ 148762306a36Sopenharmony_ci struct f_midi2 *midi2 = snd_kcontrol_chip(kcontrol); 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci ucontrol->value.integer.value[0] = midi2->operation_mode; 149062306a36Sopenharmony_ci return 0; 149162306a36Sopenharmony_ci} 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_cistatic const struct snd_kcontrol_new operation_mode_ctl = { 149462306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI, 149562306a36Sopenharmony_ci .name = "Operation Mode", 149662306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 149762306a36Sopenharmony_ci .info = f_midi2_operation_mode_info, 149862306a36Sopenharmony_ci .get = f_midi2_operation_mode_get, 149962306a36Sopenharmony_ci}; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci/* 150262306a36Sopenharmony_ci * ALSA UMP instance creation / deletion 150362306a36Sopenharmony_ci */ 150462306a36Sopenharmony_cistatic void f_midi2_free_card(struct f_midi2 *midi2) 150562306a36Sopenharmony_ci{ 150662306a36Sopenharmony_ci if (midi2->card) { 150762306a36Sopenharmony_ci snd_card_free_when_closed(midi2->card); 150862306a36Sopenharmony_ci midi2->card = NULL; 150962306a36Sopenharmony_ci } 151062306a36Sopenharmony_ci} 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci/* use a reverse direction for the gadget host */ 151362306a36Sopenharmony_cistatic int reverse_dir(int dir) 151462306a36Sopenharmony_ci{ 151562306a36Sopenharmony_ci if (!dir || dir == SNDRV_UMP_DIR_BIDIRECTION) 151662306a36Sopenharmony_ci return dir; 151762306a36Sopenharmony_ci return (dir == SNDRV_UMP_DIR_OUTPUT) ? 151862306a36Sopenharmony_ci SNDRV_UMP_DIR_INPUT : SNDRV_UMP_DIR_OUTPUT; 151962306a36Sopenharmony_ci} 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_cistatic int f_midi2_create_card(struct f_midi2 *midi2) 152262306a36Sopenharmony_ci{ 152362306a36Sopenharmony_ci struct snd_card *card; 152462306a36Sopenharmony_ci struct snd_ump_endpoint *ump; 152562306a36Sopenharmony_ci struct f_midi2_ep *ep; 152662306a36Sopenharmony_ci int i, id, blk, err; 152762306a36Sopenharmony_ci __be32 sw; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci err = snd_card_new(&midi2->gadget->dev, -1, NULL, THIS_MODULE, 0, 153062306a36Sopenharmony_ci &card); 153162306a36Sopenharmony_ci if (err < 0) 153262306a36Sopenharmony_ci return err; 153362306a36Sopenharmony_ci midi2->card = card; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci strcpy(card->driver, "f_midi2"); 153662306a36Sopenharmony_ci strcpy(card->shortname, "MIDI 2.0 Gadget"); 153762306a36Sopenharmony_ci strcpy(card->longname, "MIDI 2.0 Gadget"); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci id = 0; 154062306a36Sopenharmony_ci for (i = 0; i < midi2->num_eps; i++) { 154162306a36Sopenharmony_ci ep = &midi2->midi2_eps[i]; 154262306a36Sopenharmony_ci err = snd_ump_endpoint_new(card, "MIDI 2.0 Gadget", id, 154362306a36Sopenharmony_ci 1, 1, &ump); 154462306a36Sopenharmony_ci if (err < 0) 154562306a36Sopenharmony_ci goto error; 154662306a36Sopenharmony_ci id++; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci ep->ump = ump; 154962306a36Sopenharmony_ci ump->no_process_stream = true; 155062306a36Sopenharmony_ci ump->private_data = ep; 155162306a36Sopenharmony_ci ump->ops = &f_midi2_ump_ops; 155262306a36Sopenharmony_ci if (midi2->info.static_block) 155362306a36Sopenharmony_ci ump->info.flags |= SNDRV_UMP_EP_INFO_STATIC_BLOCKS; 155462306a36Sopenharmony_ci ump->info.protocol_caps = (ep->info.protocol_caps & 3) << 8; 155562306a36Sopenharmony_ci ump->info.protocol = (ep->info.protocol & 3) << 8; 155662306a36Sopenharmony_ci ump->info.version = 0x0101; 155762306a36Sopenharmony_ci ump->info.family_id = ep->info.family; 155862306a36Sopenharmony_ci ump->info.model_id = ep->info.model; 155962306a36Sopenharmony_ci ump->info.manufacturer_id = ep->info.manufacturer & 0xffffff; 156062306a36Sopenharmony_ci sw = cpu_to_be32(ep->info.sw_revision); 156162306a36Sopenharmony_ci memcpy(ump->info.sw_revision, &sw, 4); 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci strscpy(ump->info.name, ump_ep_name(ep), 156462306a36Sopenharmony_ci sizeof(ump->info.name)); 156562306a36Sopenharmony_ci strscpy(ump->info.product_id, ump_product_id(ep), 156662306a36Sopenharmony_ci sizeof(ump->info.product_id)); 156762306a36Sopenharmony_ci strscpy(ump->core.name, ump->info.name, sizeof(ump->core.name)); 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci for (blk = 0; blk < ep->num_blks; blk++) { 157062306a36Sopenharmony_ci const struct f_midi2_block_info *b = &ep->blks[blk].info; 157162306a36Sopenharmony_ci struct snd_ump_block *fb; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci err = snd_ump_block_new(ump, blk, 157462306a36Sopenharmony_ci reverse_dir(b->direction), 157562306a36Sopenharmony_ci b->first_group, b->num_groups, 157662306a36Sopenharmony_ci &ep->blks[blk].fb); 157762306a36Sopenharmony_ci if (err < 0) 157862306a36Sopenharmony_ci goto error; 157962306a36Sopenharmony_ci fb = ep->blks[blk].fb; 158062306a36Sopenharmony_ci fb->info.active = !!b->active; 158162306a36Sopenharmony_ci fb->info.midi_ci_version = b->midi_ci_version; 158262306a36Sopenharmony_ci fb->info.ui_hint = reverse_dir(b->ui_hint); 158362306a36Sopenharmony_ci fb->info.sysex8_streams = b->sysex8_streams; 158462306a36Sopenharmony_ci fb->info.flags |= b->is_midi1; 158562306a36Sopenharmony_ci strscpy(fb->info.name, ump_fb_name(b), 158662306a36Sopenharmony_ci sizeof(fb->info.name)); 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci } 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci for (i = 0; i < midi2->num_eps; i++) { 159162306a36Sopenharmony_ci err = snd_ump_attach_legacy_rawmidi(midi2->midi2_eps[i].ump, 159262306a36Sopenharmony_ci "Legacy MIDI", id); 159362306a36Sopenharmony_ci if (err < 0) 159462306a36Sopenharmony_ci goto error; 159562306a36Sopenharmony_ci id++; 159662306a36Sopenharmony_ci } 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&operation_mode_ctl, midi2)); 159962306a36Sopenharmony_ci if (err < 0) 160062306a36Sopenharmony_ci goto error; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci err = snd_card_register(card); 160362306a36Sopenharmony_ci if (err < 0) 160462306a36Sopenharmony_ci goto error; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci return 0; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci error: 160962306a36Sopenharmony_ci f_midi2_free_card(midi2); 161062306a36Sopenharmony_ci return err; 161162306a36Sopenharmony_ci} 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci/* 161462306a36Sopenharmony_ci * Creation of USB descriptors 161562306a36Sopenharmony_ci */ 161662306a36Sopenharmony_cistruct f_midi2_usb_config { 161762306a36Sopenharmony_ci struct usb_descriptor_header **list; 161862306a36Sopenharmony_ci unsigned int size; 161962306a36Sopenharmony_ci unsigned int alloc; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci /* MIDI 1.0 jacks */ 162262306a36Sopenharmony_ci unsigned char jack_in, jack_out, jack_id; 162362306a36Sopenharmony_ci struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES]; 162462306a36Sopenharmony_ci struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES]; 162562306a36Sopenharmony_ci}; 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_cistatic int append_config(struct f_midi2_usb_config *config, void *d) 162862306a36Sopenharmony_ci{ 162962306a36Sopenharmony_ci unsigned int size; 163062306a36Sopenharmony_ci void *buf; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci if (config->size + 2 >= config->alloc) { 163362306a36Sopenharmony_ci size = config->size + 16; 163462306a36Sopenharmony_ci buf = krealloc(config->list, size * sizeof(void *), GFP_KERNEL); 163562306a36Sopenharmony_ci if (!buf) 163662306a36Sopenharmony_ci return -ENOMEM; 163762306a36Sopenharmony_ci config->list = buf; 163862306a36Sopenharmony_ci config->alloc = size; 163962306a36Sopenharmony_ci } 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci config->list[config->size] = d; 164262306a36Sopenharmony_ci config->size++; 164362306a36Sopenharmony_ci config->list[config->size] = NULL; 164462306a36Sopenharmony_ci return 0; 164562306a36Sopenharmony_ci} 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_cistatic int append_configs(struct f_midi2_usb_config *config, void **d) 164862306a36Sopenharmony_ci{ 164962306a36Sopenharmony_ci int err; 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci for (; *d; d++) { 165262306a36Sopenharmony_ci err = append_config(config, *d); 165362306a36Sopenharmony_ci if (err) 165462306a36Sopenharmony_ci return err; 165562306a36Sopenharmony_ci } 165662306a36Sopenharmony_ci return 0; 165762306a36Sopenharmony_ci} 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_cistatic int append_midi1_in_jack(struct f_midi2 *midi2, 166062306a36Sopenharmony_ci struct f_midi2_usb_config *config, 166162306a36Sopenharmony_ci struct midi1_cable_mapping *map, 166262306a36Sopenharmony_ci unsigned int type) 166362306a36Sopenharmony_ci{ 166462306a36Sopenharmony_ci struct usb_midi_in_jack_descriptor *jack = 166562306a36Sopenharmony_ci &config->jack_ins[config->jack_in++]; 166662306a36Sopenharmony_ci int id = ++config->jack_id; 166762306a36Sopenharmony_ci int err; 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci jack->bLength = 0x06; 167062306a36Sopenharmony_ci jack->bDescriptorType = USB_DT_CS_INTERFACE; 167162306a36Sopenharmony_ci jack->bDescriptorSubtype = USB_MS_MIDI_IN_JACK; 167262306a36Sopenharmony_ci jack->bJackType = type; 167362306a36Sopenharmony_ci jack->bJackID = id; 167462306a36Sopenharmony_ci /* use the corresponding block name as jack name */ 167562306a36Sopenharmony_ci if (map->ep) 167662306a36Sopenharmony_ci jack->iJack = map->ep->blks[map->block].string_id; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci err = append_config(config, jack); 167962306a36Sopenharmony_ci if (err < 0) 168062306a36Sopenharmony_ci return err; 168162306a36Sopenharmony_ci return id; 168262306a36Sopenharmony_ci} 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_cistatic int append_midi1_out_jack(struct f_midi2 *midi2, 168562306a36Sopenharmony_ci struct f_midi2_usb_config *config, 168662306a36Sopenharmony_ci struct midi1_cable_mapping *map, 168762306a36Sopenharmony_ci unsigned int type, unsigned int source) 168862306a36Sopenharmony_ci{ 168962306a36Sopenharmony_ci struct usb_midi_out_jack_descriptor_1 *jack = 169062306a36Sopenharmony_ci &config->jack_outs[config->jack_out++]; 169162306a36Sopenharmony_ci int id = ++config->jack_id; 169262306a36Sopenharmony_ci int err; 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci jack->bLength = 0x09; 169562306a36Sopenharmony_ci jack->bDescriptorType = USB_DT_CS_INTERFACE; 169662306a36Sopenharmony_ci jack->bDescriptorSubtype = USB_MS_MIDI_OUT_JACK; 169762306a36Sopenharmony_ci jack->bJackType = type; 169862306a36Sopenharmony_ci jack->bJackID = id; 169962306a36Sopenharmony_ci jack->bNrInputPins = 1; 170062306a36Sopenharmony_ci jack->pins[0].baSourceID = source; 170162306a36Sopenharmony_ci jack->pins[0].baSourcePin = 0x01; 170262306a36Sopenharmony_ci /* use the corresponding block name as jack name */ 170362306a36Sopenharmony_ci if (map->ep) 170462306a36Sopenharmony_ci jack->iJack = map->ep->blks[map->block].string_id; 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci err = append_config(config, jack); 170762306a36Sopenharmony_ci if (err < 0) 170862306a36Sopenharmony_ci return err; 170962306a36Sopenharmony_ci return id; 171062306a36Sopenharmony_ci} 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_cistatic int f_midi2_create_usb_configs(struct f_midi2 *midi2, 171362306a36Sopenharmony_ci struct f_midi2_usb_config *config, 171462306a36Sopenharmony_ci int speed) 171562306a36Sopenharmony_ci{ 171662306a36Sopenharmony_ci void **midi1_in_eps, **midi1_out_eps; 171762306a36Sopenharmony_ci int i, jack, total; 171862306a36Sopenharmony_ci int err; 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci switch (speed) { 172162306a36Sopenharmony_ci default: 172262306a36Sopenharmony_ci case USB_SPEED_HIGH: 172362306a36Sopenharmony_ci midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(512); 172462306a36Sopenharmony_ci midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(512); 172562306a36Sopenharmony_ci for (i = 0; i < midi2->num_eps; i++) 172662306a36Sopenharmony_ci midi2_midi2_ep_out_desc[i].wMaxPacketSize = 172762306a36Sopenharmony_ci cpu_to_le16(512); 172862306a36Sopenharmony_ci fallthrough; 172962306a36Sopenharmony_ci case USB_SPEED_FULL: 173062306a36Sopenharmony_ci midi1_in_eps = midi2_midi1_ep_in_descs; 173162306a36Sopenharmony_ci midi1_out_eps = midi2_midi1_ep_out_descs; 173262306a36Sopenharmony_ci break; 173362306a36Sopenharmony_ci case USB_SPEED_SUPER: 173462306a36Sopenharmony_ci midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(1024); 173562306a36Sopenharmony_ci midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(1024); 173662306a36Sopenharmony_ci for (i = 0; i < midi2->num_eps; i++) 173762306a36Sopenharmony_ci midi2_midi2_ep_out_desc[i].wMaxPacketSize = 173862306a36Sopenharmony_ci cpu_to_le16(1024); 173962306a36Sopenharmony_ci midi1_in_eps = midi2_midi1_ep_in_ss_descs; 174062306a36Sopenharmony_ci midi1_out_eps = midi2_midi1_ep_out_ss_descs; 174162306a36Sopenharmony_ci break; 174262306a36Sopenharmony_ci } 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci err = append_configs(config, midi2_audio_descs); 174562306a36Sopenharmony_ci if (err < 0) 174662306a36Sopenharmony_ci return err; 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci if (midi2->num_midi1_in && midi2->num_midi1_out) 174962306a36Sopenharmony_ci midi2_midi1_if_desc.bNumEndpoints = 2; 175062306a36Sopenharmony_ci else 175162306a36Sopenharmony_ci midi2_midi1_if_desc.bNumEndpoints = 1; 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci err = append_configs(config, midi2_midi1_descs); 175462306a36Sopenharmony_ci if (err < 0) 175562306a36Sopenharmony_ci return err; 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci total = USB_DT_MS_HEADER_SIZE; 175862306a36Sopenharmony_ci if (midi2->num_midi1_out) { 175962306a36Sopenharmony_ci midi2_midi1_ep_out_class_desc.bLength = 176062306a36Sopenharmony_ci USB_DT_MS_ENDPOINT_SIZE(midi2->num_midi1_out); 176162306a36Sopenharmony_ci total += midi2_midi1_ep_out_class_desc.bLength; 176262306a36Sopenharmony_ci midi2_midi1_ep_out_class_desc.bNumEmbMIDIJack = 176362306a36Sopenharmony_ci midi2->num_midi1_out; 176462306a36Sopenharmony_ci total += midi2->num_midi1_out * 176562306a36Sopenharmony_ci (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1)); 176662306a36Sopenharmony_ci for (i = 0; i < midi2->num_midi1_out; i++) { 176762306a36Sopenharmony_ci jack = append_midi1_in_jack(midi2, config, 176862306a36Sopenharmony_ci &midi2->in_cable_mapping[i], 176962306a36Sopenharmony_ci USB_MS_EMBEDDED); 177062306a36Sopenharmony_ci if (jack < 0) 177162306a36Sopenharmony_ci return jack; 177262306a36Sopenharmony_ci midi2_midi1_ep_out_class_desc.baAssocJackID[i] = jack; 177362306a36Sopenharmony_ci jack = append_midi1_out_jack(midi2, config, 177462306a36Sopenharmony_ci &midi2->in_cable_mapping[i], 177562306a36Sopenharmony_ci USB_MS_EXTERNAL, jack); 177662306a36Sopenharmony_ci if (jack < 0) 177762306a36Sopenharmony_ci return jack; 177862306a36Sopenharmony_ci } 177962306a36Sopenharmony_ci } 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci if (midi2->num_midi1_in) { 178262306a36Sopenharmony_ci midi2_midi1_ep_in_class_desc.bLength = 178362306a36Sopenharmony_ci USB_DT_MS_ENDPOINT_SIZE(midi2->num_midi1_in); 178462306a36Sopenharmony_ci total += midi2_midi1_ep_in_class_desc.bLength; 178562306a36Sopenharmony_ci midi2_midi1_ep_in_class_desc.bNumEmbMIDIJack = 178662306a36Sopenharmony_ci midi2->num_midi1_in; 178762306a36Sopenharmony_ci total += midi2->num_midi1_in * 178862306a36Sopenharmony_ci (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1)); 178962306a36Sopenharmony_ci for (i = 0; i < midi2->num_midi1_in; i++) { 179062306a36Sopenharmony_ci jack = append_midi1_in_jack(midi2, config, 179162306a36Sopenharmony_ci &midi2->out_cable_mapping[i], 179262306a36Sopenharmony_ci USB_MS_EXTERNAL); 179362306a36Sopenharmony_ci if (jack < 0) 179462306a36Sopenharmony_ci return jack; 179562306a36Sopenharmony_ci jack = append_midi1_out_jack(midi2, config, 179662306a36Sopenharmony_ci &midi2->out_cable_mapping[i], 179762306a36Sopenharmony_ci USB_MS_EMBEDDED, jack); 179862306a36Sopenharmony_ci if (jack < 0) 179962306a36Sopenharmony_ci return jack; 180062306a36Sopenharmony_ci midi2_midi1_ep_in_class_desc.baAssocJackID[i] = jack; 180162306a36Sopenharmony_ci } 180262306a36Sopenharmony_ci } 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci midi2_midi1_class_desc.wTotalLength = cpu_to_le16(total); 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci if (midi2->num_midi1_out) { 180762306a36Sopenharmony_ci err = append_configs(config, midi1_out_eps); 180862306a36Sopenharmony_ci if (err < 0) 180962306a36Sopenharmony_ci return err; 181062306a36Sopenharmony_ci } 181162306a36Sopenharmony_ci if (midi2->num_midi1_in) { 181262306a36Sopenharmony_ci err = append_configs(config, midi1_in_eps); 181362306a36Sopenharmony_ci if (err < 0) 181462306a36Sopenharmony_ci return err; 181562306a36Sopenharmony_ci } 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci err = append_configs(config, midi2_midi2_descs); 181862306a36Sopenharmony_ci if (err < 0) 181962306a36Sopenharmony_ci return err; 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci for (i = 0; i < midi2->num_eps; i++) { 182262306a36Sopenharmony_ci err = append_config(config, &midi2_midi2_ep_out_desc[i]); 182362306a36Sopenharmony_ci if (err < 0) 182462306a36Sopenharmony_ci return err; 182562306a36Sopenharmony_ci if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS) { 182662306a36Sopenharmony_ci err = append_config(config, &midi2_midi2_ep_out_ss_comp_desc); 182762306a36Sopenharmony_ci if (err < 0) 182862306a36Sopenharmony_ci return err; 182962306a36Sopenharmony_ci } 183062306a36Sopenharmony_ci err = append_config(config, &midi2_midi2_ep_out_class_desc[i]); 183162306a36Sopenharmony_ci if (err < 0) 183262306a36Sopenharmony_ci return err; 183362306a36Sopenharmony_ci err = append_config(config, &midi2_midi2_ep_in_desc[i]); 183462306a36Sopenharmony_ci if (err < 0) 183562306a36Sopenharmony_ci return err; 183662306a36Sopenharmony_ci if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS) { 183762306a36Sopenharmony_ci err = append_config(config, &midi2_midi2_ep_in_ss_comp_desc); 183862306a36Sopenharmony_ci if (err < 0) 183962306a36Sopenharmony_ci return err; 184062306a36Sopenharmony_ci } 184162306a36Sopenharmony_ci err = append_config(config, &midi2_midi2_ep_in_class_desc[i]); 184262306a36Sopenharmony_ci if (err < 0) 184362306a36Sopenharmony_ci return err; 184462306a36Sopenharmony_ci } 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci return 0; 184762306a36Sopenharmony_ci} 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_cistatic void f_midi2_free_usb_configs(struct f_midi2_usb_config *config) 185062306a36Sopenharmony_ci{ 185162306a36Sopenharmony_ci kfree(config->list); 185262306a36Sopenharmony_ci memset(config, 0, sizeof(*config)); 185362306a36Sopenharmony_ci} 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci/* as we use the static descriptors for simplicity, serialize bind call */ 185662306a36Sopenharmony_cistatic DEFINE_MUTEX(f_midi2_desc_mutex); 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci/* fill MIDI2 EP class-specific descriptor */ 185962306a36Sopenharmony_cistatic void fill_midi2_class_desc(struct f_midi2_ep *ep, 186062306a36Sopenharmony_ci struct usb_ms20_endpoint_descriptor_32 *cdesc) 186162306a36Sopenharmony_ci{ 186262306a36Sopenharmony_ci int blk; 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci cdesc->bLength = USB_DT_MS20_ENDPOINT_SIZE(ep->num_blks); 186562306a36Sopenharmony_ci cdesc->bDescriptorType = USB_DT_CS_ENDPOINT; 186662306a36Sopenharmony_ci cdesc->bDescriptorSubtype = USB_MS_GENERAL_2_0; 186762306a36Sopenharmony_ci cdesc->bNumGrpTrmBlock = ep->num_blks; 186862306a36Sopenharmony_ci for (blk = 0; blk < ep->num_blks; blk++) 186962306a36Sopenharmony_ci cdesc->baAssoGrpTrmBlkID[blk] = ep->blks[blk].gtb_id; 187062306a36Sopenharmony_ci} 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci/* initialize MIDI2 EP-in */ 187362306a36Sopenharmony_cistatic int f_midi2_init_midi2_ep_in(struct f_midi2 *midi2, int index) 187462306a36Sopenharmony_ci{ 187562306a36Sopenharmony_ci struct f_midi2_ep *ep = &midi2->midi2_eps[index]; 187662306a36Sopenharmony_ci struct usb_endpoint_descriptor *desc = &midi2_midi2_ep_in_desc[index]; 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci desc->bLength = USB_DT_ENDPOINT_SIZE; 187962306a36Sopenharmony_ci desc->bDescriptorType = USB_DT_ENDPOINT; 188062306a36Sopenharmony_ci desc->bEndpointAddress = USB_DIR_IN; 188162306a36Sopenharmony_ci desc->bmAttributes = USB_ENDPOINT_XFER_INT; 188262306a36Sopenharmony_ci desc->wMaxPacketSize = cpu_to_le16(EP_MAX_PACKET_INT); 188362306a36Sopenharmony_ci desc->bInterval = 1; 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci fill_midi2_class_desc(ep, &midi2_midi2_ep_in_class_desc[index]); 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci return f_midi2_init_ep(midi2, ep, &ep->ep_in, desc, 188862306a36Sopenharmony_ci f_midi2_ep_in_complete); 188962306a36Sopenharmony_ci} 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci/* initialize MIDI2 EP-out */ 189262306a36Sopenharmony_cistatic int f_midi2_init_midi2_ep_out(struct f_midi2 *midi2, int index) 189362306a36Sopenharmony_ci{ 189462306a36Sopenharmony_ci struct f_midi2_ep *ep = &midi2->midi2_eps[index]; 189562306a36Sopenharmony_ci struct usb_endpoint_descriptor *desc = &midi2_midi2_ep_out_desc[index]; 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci desc->bLength = USB_DT_ENDPOINT_SIZE; 189862306a36Sopenharmony_ci desc->bDescriptorType = USB_DT_ENDPOINT; 189962306a36Sopenharmony_ci desc->bEndpointAddress = USB_DIR_OUT; 190062306a36Sopenharmony_ci desc->bmAttributes = USB_ENDPOINT_XFER_BULK; 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci fill_midi2_class_desc(ep, &midi2_midi2_ep_out_class_desc[index]); 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci return f_midi2_init_ep(midi2, ep, &ep->ep_out, desc, 190562306a36Sopenharmony_ci f_midi2_ep_out_complete); 190662306a36Sopenharmony_ci} 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci/* gadget function bind callback */ 190962306a36Sopenharmony_cistatic int f_midi2_bind(struct usb_configuration *c, struct usb_function *f) 191062306a36Sopenharmony_ci{ 191162306a36Sopenharmony_ci struct usb_composite_dev *cdev = c->cdev; 191262306a36Sopenharmony_ci struct f_midi2 *midi2 = func_to_midi2(f); 191362306a36Sopenharmony_ci struct f_midi2_ep *ep; 191462306a36Sopenharmony_ci struct f_midi2_usb_config config = {}; 191562306a36Sopenharmony_ci struct usb_gadget_strings string_fn = { 191662306a36Sopenharmony_ci .language = 0x0409, /* en-us */ 191762306a36Sopenharmony_ci .strings = midi2->string_defs, 191862306a36Sopenharmony_ci }; 191962306a36Sopenharmony_ci struct usb_gadget_strings *strings[] = { 192062306a36Sopenharmony_ci &string_fn, 192162306a36Sopenharmony_ci NULL, 192262306a36Sopenharmony_ci }; 192362306a36Sopenharmony_ci int i, blk, status; 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci midi2->gadget = cdev->gadget; 192662306a36Sopenharmony_ci midi2->operation_mode = MIDI_OP_MODE_UNSET; 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci status = f_midi2_create_card(midi2); 192962306a36Sopenharmony_ci if (status < 0) 193062306a36Sopenharmony_ci goto fail_register; 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci /* maybe allocate device-global string ID */ 193362306a36Sopenharmony_ci midi2->strings = usb_gstrings_attach(c->cdev, strings, 193462306a36Sopenharmony_ci midi2->total_blocks + 1); 193562306a36Sopenharmony_ci if (IS_ERR(midi2->strings)) { 193662306a36Sopenharmony_ci status = PTR_ERR(midi2->strings); 193762306a36Sopenharmony_ci goto fail_string; 193862306a36Sopenharmony_ci } 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci mutex_lock(&f_midi2_desc_mutex); 194162306a36Sopenharmony_ci midi2_midi1_if_desc.iInterface = midi2->strings[STR_IFACE].id; 194262306a36Sopenharmony_ci midi2_midi2_if_desc.iInterface = midi2->strings[STR_IFACE].id; 194362306a36Sopenharmony_ci for (i = 0; i < midi2->num_eps; i++) { 194462306a36Sopenharmony_ci ep = &midi2->midi2_eps[i]; 194562306a36Sopenharmony_ci for (blk = 0; blk < ep->num_blks; blk++) 194662306a36Sopenharmony_ci ep->blks[blk].string_id = 194762306a36Sopenharmony_ci midi2->strings[gtb_to_str_id(ep->blks[blk].gtb_id)].id; 194862306a36Sopenharmony_ci } 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci midi2_midi2_if_desc.bNumEndpoints = midi2->num_eps * 2; 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci /* audio interface */ 195362306a36Sopenharmony_ci status = usb_interface_id(c, f); 195462306a36Sopenharmony_ci if (status < 0) 195562306a36Sopenharmony_ci goto fail; 195662306a36Sopenharmony_ci midi2_audio_if_desc.bInterfaceNumber = status; 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci /* MIDI streaming */ 195962306a36Sopenharmony_ci status = usb_interface_id(c, f); 196062306a36Sopenharmony_ci if (status < 0) 196162306a36Sopenharmony_ci goto fail; 196262306a36Sopenharmony_ci midi2->midi_if = status; 196362306a36Sopenharmony_ci midi2_midi1_if_desc.bInterfaceNumber = status; 196462306a36Sopenharmony_ci midi2_midi2_if_desc.bInterfaceNumber = status; 196562306a36Sopenharmony_ci midi2_audio_class_desc.baInterfaceNr[0] = status; 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci /* allocate instance-specific endpoints */ 196862306a36Sopenharmony_ci if (midi2->midi2_eps[0].blks[0].info.direction != SNDRV_UMP_DIR_OUTPUT) { 196962306a36Sopenharmony_ci status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_in, 197062306a36Sopenharmony_ci &midi2_midi1_ep_in_desc, 197162306a36Sopenharmony_ci f_midi2_midi1_ep_in_complete); 197262306a36Sopenharmony_ci if (status) 197362306a36Sopenharmony_ci goto fail; 197462306a36Sopenharmony_ci } 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci if (midi2->midi2_eps[0].blks[0].info.direction != SNDRV_UMP_DIR_INPUT) { 197762306a36Sopenharmony_ci status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_out, 197862306a36Sopenharmony_ci &midi2_midi1_ep_out_desc, 197962306a36Sopenharmony_ci f_midi2_midi1_ep_out_complete); 198062306a36Sopenharmony_ci if (status) 198162306a36Sopenharmony_ci goto fail; 198262306a36Sopenharmony_ci } 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci for (i = 0; i < midi2->num_eps; i++) { 198562306a36Sopenharmony_ci status = f_midi2_init_midi2_ep_in(midi2, i); 198662306a36Sopenharmony_ci if (status) 198762306a36Sopenharmony_ci goto fail; 198862306a36Sopenharmony_ci status = f_midi2_init_midi2_ep_out(midi2, i); 198962306a36Sopenharmony_ci if (status) 199062306a36Sopenharmony_ci goto fail; 199162306a36Sopenharmony_ci } 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_FULL); 199462306a36Sopenharmony_ci if (status < 0) 199562306a36Sopenharmony_ci goto fail; 199662306a36Sopenharmony_ci f->fs_descriptors = usb_copy_descriptors(config.list); 199762306a36Sopenharmony_ci if (!f->fs_descriptors) { 199862306a36Sopenharmony_ci status = -ENOMEM; 199962306a36Sopenharmony_ci goto fail; 200062306a36Sopenharmony_ci } 200162306a36Sopenharmony_ci f_midi2_free_usb_configs(&config); 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_HIGH); 200462306a36Sopenharmony_ci if (status < 0) 200562306a36Sopenharmony_ci goto fail; 200662306a36Sopenharmony_ci f->hs_descriptors = usb_copy_descriptors(config.list); 200762306a36Sopenharmony_ci if (!f->hs_descriptors) { 200862306a36Sopenharmony_ci status = -ENOMEM; 200962306a36Sopenharmony_ci goto fail; 201062306a36Sopenharmony_ci } 201162306a36Sopenharmony_ci f_midi2_free_usb_configs(&config); 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_SUPER); 201462306a36Sopenharmony_ci if (status < 0) 201562306a36Sopenharmony_ci goto fail; 201662306a36Sopenharmony_ci f->ss_descriptors = usb_copy_descriptors(config.list); 201762306a36Sopenharmony_ci if (!f->ss_descriptors) { 201862306a36Sopenharmony_ci status = -ENOMEM; 201962306a36Sopenharmony_ci goto fail; 202062306a36Sopenharmony_ci } 202162306a36Sopenharmony_ci f_midi2_free_usb_configs(&config); 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci mutex_unlock(&f_midi2_desc_mutex); 202462306a36Sopenharmony_ci return 0; 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_cifail: 202762306a36Sopenharmony_ci f_midi2_free_usb_configs(&config); 202862306a36Sopenharmony_ci mutex_unlock(&f_midi2_desc_mutex); 202962306a36Sopenharmony_ci usb_free_all_descriptors(f); 203062306a36Sopenharmony_cifail_string: 203162306a36Sopenharmony_ci f_midi2_free_card(midi2); 203262306a36Sopenharmony_cifail_register: 203362306a36Sopenharmony_ci ERROR(midi2, "%s: can't bind, err %d\n", f->name, status); 203462306a36Sopenharmony_ci return status; 203562306a36Sopenharmony_ci} 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci/* gadget function unbind callback */ 203862306a36Sopenharmony_cistatic void f_midi2_unbind(struct usb_configuration *c, struct usb_function *f) 203962306a36Sopenharmony_ci{ 204062306a36Sopenharmony_ci struct f_midi2 *midi2 = func_to_midi2(f); 204162306a36Sopenharmony_ci int i; 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci f_midi2_free_card(midi2); 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci f_midi2_free_ep(&midi2->midi1_ep_in); 204662306a36Sopenharmony_ci f_midi2_free_ep(&midi2->midi1_ep_out); 204762306a36Sopenharmony_ci for (i = 0; i < midi2->num_eps; i++) { 204862306a36Sopenharmony_ci f_midi2_free_ep(&midi2->midi2_eps[i].ep_in); 204962306a36Sopenharmony_ci f_midi2_free_ep(&midi2->midi2_eps[i].ep_out); 205062306a36Sopenharmony_ci } 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci usb_free_all_descriptors(f); 205362306a36Sopenharmony_ci} 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci/* 205662306a36Sopenharmony_ci * ConfigFS interface 205762306a36Sopenharmony_ci */ 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci/* type conversion helpers */ 206062306a36Sopenharmony_cistatic inline struct f_midi2_opts *to_f_midi2_opts(struct config_item *item) 206162306a36Sopenharmony_ci{ 206262306a36Sopenharmony_ci return container_of(to_config_group(item), struct f_midi2_opts, 206362306a36Sopenharmony_ci func_inst.group); 206462306a36Sopenharmony_ci} 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_cistatic inline struct f_midi2_ep_opts * 206762306a36Sopenharmony_cito_f_midi2_ep_opts(struct config_item *item) 206862306a36Sopenharmony_ci{ 206962306a36Sopenharmony_ci return container_of(to_config_group(item), struct f_midi2_ep_opts, 207062306a36Sopenharmony_ci group); 207162306a36Sopenharmony_ci} 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_cistatic inline struct f_midi2_block_opts * 207462306a36Sopenharmony_cito_f_midi2_block_opts(struct config_item *item) 207562306a36Sopenharmony_ci{ 207662306a36Sopenharmony_ci return container_of(to_config_group(item), struct f_midi2_block_opts, 207762306a36Sopenharmony_ci group); 207862306a36Sopenharmony_ci} 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci/* trim the string to be usable for EP and FB name strings */ 208162306a36Sopenharmony_cistatic void make_name_string(char *s) 208262306a36Sopenharmony_ci{ 208362306a36Sopenharmony_ci char *p; 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci p = strchr(s, '\n'); 208662306a36Sopenharmony_ci if (p) 208762306a36Sopenharmony_ci *p = 0; 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci p = s + strlen(s); 209062306a36Sopenharmony_ci for (; p > s && isspace(*p); p--) 209162306a36Sopenharmony_ci *p = 0; 209262306a36Sopenharmony_ci} 209362306a36Sopenharmony_ci 209462306a36Sopenharmony_ci/* configfs helpers: generic show/store for unisnged int */ 209562306a36Sopenharmony_cistatic ssize_t f_midi2_opts_uint_show(struct f_midi2_opts *opts, 209662306a36Sopenharmony_ci u32 val, const char *format, char *page) 209762306a36Sopenharmony_ci{ 209862306a36Sopenharmony_ci int result; 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci mutex_lock(&opts->lock); 210162306a36Sopenharmony_ci result = sprintf(page, format, val); 210262306a36Sopenharmony_ci mutex_unlock(&opts->lock); 210362306a36Sopenharmony_ci return result; 210462306a36Sopenharmony_ci} 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_cistatic ssize_t f_midi2_opts_uint_store(struct f_midi2_opts *opts, 210762306a36Sopenharmony_ci u32 *valp, u32 minval, u32 maxval, 210862306a36Sopenharmony_ci const char *page, size_t len) 210962306a36Sopenharmony_ci{ 211062306a36Sopenharmony_ci int ret; 211162306a36Sopenharmony_ci u32 val; 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci mutex_lock(&opts->lock); 211462306a36Sopenharmony_ci if (opts->refcnt) { 211562306a36Sopenharmony_ci ret = -EBUSY; 211662306a36Sopenharmony_ci goto end; 211762306a36Sopenharmony_ci } 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci ret = kstrtou32(page, 0, &val); 212062306a36Sopenharmony_ci if (ret) 212162306a36Sopenharmony_ci goto end; 212262306a36Sopenharmony_ci if (val < minval || val > maxval) { 212362306a36Sopenharmony_ci ret = -EINVAL; 212462306a36Sopenharmony_ci goto end; 212562306a36Sopenharmony_ci } 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci *valp = val; 212862306a36Sopenharmony_ci ret = len; 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ciend: 213162306a36Sopenharmony_ci mutex_unlock(&opts->lock); 213262306a36Sopenharmony_ci return ret; 213362306a36Sopenharmony_ci} 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci/* generic store for bool */ 213662306a36Sopenharmony_cistatic ssize_t f_midi2_opts_bool_store(struct f_midi2_opts *opts, 213762306a36Sopenharmony_ci bool *valp, const char *page, size_t len) 213862306a36Sopenharmony_ci{ 213962306a36Sopenharmony_ci int ret; 214062306a36Sopenharmony_ci bool val; 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci mutex_lock(&opts->lock); 214362306a36Sopenharmony_ci if (opts->refcnt) { 214462306a36Sopenharmony_ci ret = -EBUSY; 214562306a36Sopenharmony_ci goto end; 214662306a36Sopenharmony_ci } 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci ret = kstrtobool(page, &val); 214962306a36Sopenharmony_ci if (ret) 215062306a36Sopenharmony_ci goto end; 215162306a36Sopenharmony_ci *valp = val; 215262306a36Sopenharmony_ci ret = len; 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ciend: 215562306a36Sopenharmony_ci mutex_unlock(&opts->lock); 215662306a36Sopenharmony_ci return ret; 215762306a36Sopenharmony_ci} 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci/* generic show/store for string */ 216062306a36Sopenharmony_cistatic ssize_t f_midi2_opts_str_show(struct f_midi2_opts *opts, 216162306a36Sopenharmony_ci const char *str, char *page) 216262306a36Sopenharmony_ci{ 216362306a36Sopenharmony_ci int result = 0; 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci mutex_lock(&opts->lock); 216662306a36Sopenharmony_ci if (str) 216762306a36Sopenharmony_ci result = scnprintf(page, PAGE_SIZE, "%s\n", str); 216862306a36Sopenharmony_ci mutex_unlock(&opts->lock); 216962306a36Sopenharmony_ci return result; 217062306a36Sopenharmony_ci} 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_cistatic ssize_t f_midi2_opts_str_store(struct f_midi2_opts *opts, 217362306a36Sopenharmony_ci const char **strp, size_t maxlen, 217462306a36Sopenharmony_ci const char *page, size_t len) 217562306a36Sopenharmony_ci{ 217662306a36Sopenharmony_ci char *c; 217762306a36Sopenharmony_ci int ret; 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci mutex_lock(&opts->lock); 218062306a36Sopenharmony_ci if (opts->refcnt) { 218162306a36Sopenharmony_ci ret = -EBUSY; 218262306a36Sopenharmony_ci goto end; 218362306a36Sopenharmony_ci } 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci c = kstrndup(page, min(len, maxlen), GFP_KERNEL); 218662306a36Sopenharmony_ci if (!c) { 218762306a36Sopenharmony_ci ret = -ENOMEM; 218862306a36Sopenharmony_ci goto end; 218962306a36Sopenharmony_ci } 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci kfree(*strp); 219262306a36Sopenharmony_ci make_name_string(c); 219362306a36Sopenharmony_ci *strp = c; 219462306a36Sopenharmony_ci ret = len; 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ciend: 219762306a36Sopenharmony_ci mutex_unlock(&opts->lock); 219862306a36Sopenharmony_ci return ret; 219962306a36Sopenharmony_ci} 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci/* 220262306a36Sopenharmony_ci * Definitions for UMP Block config 220362306a36Sopenharmony_ci */ 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci/* define an uint option for block */ 220662306a36Sopenharmony_ci#define F_MIDI2_BLOCK_OPT(name, format, minval, maxval) \ 220762306a36Sopenharmony_cistatic ssize_t f_midi2_block_opts_##name##_show(struct config_item *item,\ 220862306a36Sopenharmony_ci char *page) \ 220962306a36Sopenharmony_ci{ \ 221062306a36Sopenharmony_ci struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \ 221162306a36Sopenharmony_ci return f_midi2_opts_uint_show(opts->ep->opts, opts->info.name, \ 221262306a36Sopenharmony_ci format "\n", page); \ 221362306a36Sopenharmony_ci} \ 221462306a36Sopenharmony_ci \ 221562306a36Sopenharmony_cistatic ssize_t f_midi2_block_opts_##name##_store(struct config_item *item,\ 221662306a36Sopenharmony_ci const char *page, size_t len) \ 221762306a36Sopenharmony_ci{ \ 221862306a36Sopenharmony_ci struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \ 221962306a36Sopenharmony_ci return f_midi2_opts_uint_store(opts->ep->opts, &opts->info.name,\ 222062306a36Sopenharmony_ci minval, maxval, page, len); \ 222162306a36Sopenharmony_ci} \ 222262306a36Sopenharmony_ci \ 222362306a36Sopenharmony_ciCONFIGFS_ATTR(f_midi2_block_opts_, name) 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci/* define a boolean option for block */ 222662306a36Sopenharmony_ci#define F_MIDI2_BLOCK_BOOL_OPT(name) \ 222762306a36Sopenharmony_cistatic ssize_t f_midi2_block_opts_##name##_show(struct config_item *item,\ 222862306a36Sopenharmony_ci char *page) \ 222962306a36Sopenharmony_ci{ \ 223062306a36Sopenharmony_ci struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \ 223162306a36Sopenharmony_ci return f_midi2_opts_uint_show(opts->ep->opts, opts->info.name, \ 223262306a36Sopenharmony_ci "%u\n", page); \ 223362306a36Sopenharmony_ci} \ 223462306a36Sopenharmony_ci \ 223562306a36Sopenharmony_cistatic ssize_t f_midi2_block_opts_##name##_store(struct config_item *item,\ 223662306a36Sopenharmony_ci const char *page, size_t len) \ 223762306a36Sopenharmony_ci{ \ 223862306a36Sopenharmony_ci struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \ 223962306a36Sopenharmony_ci return f_midi2_opts_bool_store(opts->ep->opts, &opts->info.name,\ 224062306a36Sopenharmony_ci page, len); \ 224162306a36Sopenharmony_ci} \ 224262306a36Sopenharmony_ci \ 224362306a36Sopenharmony_ciCONFIGFS_ATTR(f_midi2_block_opts_, name) 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ciF_MIDI2_BLOCK_OPT(direction, "0x%x", 1, 3); 224662306a36Sopenharmony_ciF_MIDI2_BLOCK_OPT(first_group, "0x%x", 0, 15); 224762306a36Sopenharmony_ciF_MIDI2_BLOCK_OPT(num_groups, "0x%x", 1, 16); 224862306a36Sopenharmony_ciF_MIDI2_BLOCK_OPT(midi1_first_group, "0x%x", 0, 15); 224962306a36Sopenharmony_ciF_MIDI2_BLOCK_OPT(midi1_num_groups, "0x%x", 0, 16); 225062306a36Sopenharmony_ciF_MIDI2_BLOCK_OPT(ui_hint, "0x%x", 0, 3); 225162306a36Sopenharmony_ciF_MIDI2_BLOCK_OPT(midi_ci_version, "%u", 0, 1); 225262306a36Sopenharmony_ciF_MIDI2_BLOCK_OPT(sysex8_streams, "%u", 0, 255); 225362306a36Sopenharmony_ciF_MIDI2_BLOCK_OPT(is_midi1, "%u", 0, 2); 225462306a36Sopenharmony_ciF_MIDI2_BLOCK_BOOL_OPT(active); 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_cistatic ssize_t f_midi2_block_opts_name_show(struct config_item *item, 225762306a36Sopenharmony_ci char *page) 225862306a36Sopenharmony_ci{ 225962306a36Sopenharmony_ci struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci return f_midi2_opts_str_show(opts->ep->opts, opts->info.name, page); 226262306a36Sopenharmony_ci} 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_cistatic ssize_t f_midi2_block_opts_name_store(struct config_item *item, 226562306a36Sopenharmony_ci const char *page, size_t len) 226662306a36Sopenharmony_ci{ 226762306a36Sopenharmony_ci struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci return f_midi2_opts_str_store(opts->ep->opts, &opts->info.name, 128, 227062306a36Sopenharmony_ci page, len); 227162306a36Sopenharmony_ci} 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ciCONFIGFS_ATTR(f_midi2_block_opts_, name); 227462306a36Sopenharmony_ci 227562306a36Sopenharmony_cistatic struct configfs_attribute *f_midi2_block_attrs[] = { 227662306a36Sopenharmony_ci &f_midi2_block_opts_attr_direction, 227762306a36Sopenharmony_ci &f_midi2_block_opts_attr_first_group, 227862306a36Sopenharmony_ci &f_midi2_block_opts_attr_num_groups, 227962306a36Sopenharmony_ci &f_midi2_block_opts_attr_midi1_first_group, 228062306a36Sopenharmony_ci &f_midi2_block_opts_attr_midi1_num_groups, 228162306a36Sopenharmony_ci &f_midi2_block_opts_attr_ui_hint, 228262306a36Sopenharmony_ci &f_midi2_block_opts_attr_midi_ci_version, 228362306a36Sopenharmony_ci &f_midi2_block_opts_attr_sysex8_streams, 228462306a36Sopenharmony_ci &f_midi2_block_opts_attr_is_midi1, 228562306a36Sopenharmony_ci &f_midi2_block_opts_attr_active, 228662306a36Sopenharmony_ci &f_midi2_block_opts_attr_name, 228762306a36Sopenharmony_ci NULL, 228862306a36Sopenharmony_ci}; 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_cistatic void f_midi2_block_opts_release(struct config_item *item) 229162306a36Sopenharmony_ci{ 229262306a36Sopenharmony_ci struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ci kfree(opts->info.name); 229562306a36Sopenharmony_ci kfree(opts); 229662306a36Sopenharmony_ci} 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_cistatic struct configfs_item_operations f_midi2_block_item_ops = { 229962306a36Sopenharmony_ci .release = f_midi2_block_opts_release, 230062306a36Sopenharmony_ci}; 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_cistatic const struct config_item_type f_midi2_block_type = { 230362306a36Sopenharmony_ci .ct_item_ops = &f_midi2_block_item_ops, 230462306a36Sopenharmony_ci .ct_attrs = f_midi2_block_attrs, 230562306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 230662306a36Sopenharmony_ci}; 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci/* create a f_midi2_block_opts instance for the given block number */ 230962306a36Sopenharmony_cistatic int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts, 231062306a36Sopenharmony_ci unsigned int blk, 231162306a36Sopenharmony_ci struct f_midi2_block_opts **block_p) 231262306a36Sopenharmony_ci{ 231362306a36Sopenharmony_ci struct f_midi2_block_opts *block_opts; 231462306a36Sopenharmony_ci int ret = 0; 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci mutex_lock(&ep_opts->opts->lock); 231762306a36Sopenharmony_ci if (ep_opts->opts->refcnt || ep_opts->blks[blk]) { 231862306a36Sopenharmony_ci ret = -EBUSY; 231962306a36Sopenharmony_ci goto out; 232062306a36Sopenharmony_ci } 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci block_opts = kzalloc(sizeof(*block_opts), GFP_KERNEL); 232362306a36Sopenharmony_ci if (!block_opts) { 232462306a36Sopenharmony_ci ret = -ENOMEM; 232562306a36Sopenharmony_ci goto out; 232662306a36Sopenharmony_ci } 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_ci block_opts->ep = ep_opts; 232962306a36Sopenharmony_ci block_opts->id = blk; 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci /* set up the default values */ 233262306a36Sopenharmony_ci block_opts->info.direction = SNDRV_UMP_DIR_BIDIRECTION; 233362306a36Sopenharmony_ci block_opts->info.first_group = 0; 233462306a36Sopenharmony_ci block_opts->info.num_groups = 1; 233562306a36Sopenharmony_ci block_opts->info.ui_hint = SNDRV_UMP_BLOCK_UI_HINT_BOTH; 233662306a36Sopenharmony_ci block_opts->info.active = 1; 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci ep_opts->blks[blk] = block_opts; 233962306a36Sopenharmony_ci *block_p = block_opts; 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci out: 234262306a36Sopenharmony_ci mutex_unlock(&ep_opts->opts->lock); 234362306a36Sopenharmony_ci return ret; 234462306a36Sopenharmony_ci} 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci/* make_group callback for a block */ 234762306a36Sopenharmony_cistatic struct config_group * 234862306a36Sopenharmony_cif_midi2_opts_block_make(struct config_group *group, const char *name) 234962306a36Sopenharmony_ci{ 235062306a36Sopenharmony_ci struct f_midi2_ep_opts *ep_opts; 235162306a36Sopenharmony_ci struct f_midi2_block_opts *block_opts; 235262306a36Sopenharmony_ci unsigned int blk; 235362306a36Sopenharmony_ci int ret; 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci if (strncmp(name, "block.", 6)) 235662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 235762306a36Sopenharmony_ci ret = kstrtouint(name + 6, 10, &blk); 235862306a36Sopenharmony_ci if (ret) 235962306a36Sopenharmony_ci return ERR_PTR(ret); 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci ep_opts = to_f_midi2_ep_opts(&group->cg_item); 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci if (blk >= SNDRV_UMP_MAX_BLOCKS) 236462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 236562306a36Sopenharmony_ci if (ep_opts->blks[blk]) 236662306a36Sopenharmony_ci return ERR_PTR(-EBUSY); 236762306a36Sopenharmony_ci ret = f_midi2_block_opts_create(ep_opts, blk, &block_opts); 236862306a36Sopenharmony_ci if (ret) 236962306a36Sopenharmony_ci return ERR_PTR(ret); 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci config_group_init_type_name(&block_opts->group, name, 237262306a36Sopenharmony_ci &f_midi2_block_type); 237362306a36Sopenharmony_ci return &block_opts->group; 237462306a36Sopenharmony_ci} 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_ci/* drop_item callback for a block */ 237762306a36Sopenharmony_cistatic void 237862306a36Sopenharmony_cif_midi2_opts_block_drop(struct config_group *group, struct config_item *item) 237962306a36Sopenharmony_ci{ 238062306a36Sopenharmony_ci struct f_midi2_block_opts *block_opts = to_f_midi2_block_opts(item); 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci mutex_lock(&block_opts->ep->opts->lock); 238362306a36Sopenharmony_ci block_opts->ep->blks[block_opts->id] = NULL; 238462306a36Sopenharmony_ci mutex_unlock(&block_opts->ep->opts->lock); 238562306a36Sopenharmony_ci config_item_put(item); 238662306a36Sopenharmony_ci} 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci/* 238962306a36Sopenharmony_ci * Definitions for UMP Endpoint config 239062306a36Sopenharmony_ci */ 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci/* define an uint option for EP */ 239362306a36Sopenharmony_ci#define F_MIDI2_EP_OPT(name, format, minval, maxval) \ 239462306a36Sopenharmony_cistatic ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \ 239562306a36Sopenharmony_ci char *page) \ 239662306a36Sopenharmony_ci{ \ 239762306a36Sopenharmony_ci struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \ 239862306a36Sopenharmony_ci return f_midi2_opts_uint_show(opts->opts, opts->info.name, \ 239962306a36Sopenharmony_ci format "\n", page); \ 240062306a36Sopenharmony_ci} \ 240162306a36Sopenharmony_ci \ 240262306a36Sopenharmony_cistatic ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item, \ 240362306a36Sopenharmony_ci const char *page, size_t len)\ 240462306a36Sopenharmony_ci{ \ 240562306a36Sopenharmony_ci struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \ 240662306a36Sopenharmony_ci return f_midi2_opts_uint_store(opts->opts, &opts->info.name, \ 240762306a36Sopenharmony_ci minval, maxval, page, len); \ 240862306a36Sopenharmony_ci} \ 240962306a36Sopenharmony_ci \ 241062306a36Sopenharmony_ciCONFIGFS_ATTR(f_midi2_ep_opts_, name) 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci/* define a string option for EP */ 241362306a36Sopenharmony_ci#define F_MIDI2_EP_STR_OPT(name, maxlen) \ 241462306a36Sopenharmony_cistatic ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \ 241562306a36Sopenharmony_ci char *page) \ 241662306a36Sopenharmony_ci{ \ 241762306a36Sopenharmony_ci struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \ 241862306a36Sopenharmony_ci return f_midi2_opts_str_show(opts->opts, opts->info.name, page);\ 241962306a36Sopenharmony_ci} \ 242062306a36Sopenharmony_ci \ 242162306a36Sopenharmony_cistatic ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item, \ 242262306a36Sopenharmony_ci const char *page, size_t len) \ 242362306a36Sopenharmony_ci{ \ 242462306a36Sopenharmony_ci struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \ 242562306a36Sopenharmony_ci return f_midi2_opts_str_store(opts->opts, &opts->info.name, maxlen,\ 242662306a36Sopenharmony_ci page, len); \ 242762306a36Sopenharmony_ci} \ 242862306a36Sopenharmony_ci \ 242962306a36Sopenharmony_ciCONFIGFS_ATTR(f_midi2_ep_opts_, name) 243062306a36Sopenharmony_ci 243162306a36Sopenharmony_ciF_MIDI2_EP_OPT(protocol, "0x%x", 1, 2); 243262306a36Sopenharmony_ciF_MIDI2_EP_OPT(protocol_caps, "0x%x", 1, 3); 243362306a36Sopenharmony_ciF_MIDI2_EP_OPT(manufacturer, "0x%x", 0, 0xffffff); 243462306a36Sopenharmony_ciF_MIDI2_EP_OPT(family, "0x%x", 0, 0xffff); 243562306a36Sopenharmony_ciF_MIDI2_EP_OPT(model, "0x%x", 0, 0xffff); 243662306a36Sopenharmony_ciF_MIDI2_EP_OPT(sw_revision, "0x%x", 0, 0xffffffff); 243762306a36Sopenharmony_ciF_MIDI2_EP_STR_OPT(ep_name, 128); 243862306a36Sopenharmony_ciF_MIDI2_EP_STR_OPT(product_id, 128); 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_cistatic struct configfs_attribute *f_midi2_ep_attrs[] = { 244162306a36Sopenharmony_ci &f_midi2_ep_opts_attr_protocol, 244262306a36Sopenharmony_ci &f_midi2_ep_opts_attr_protocol_caps, 244362306a36Sopenharmony_ci &f_midi2_ep_opts_attr_ep_name, 244462306a36Sopenharmony_ci &f_midi2_ep_opts_attr_product_id, 244562306a36Sopenharmony_ci &f_midi2_ep_opts_attr_manufacturer, 244662306a36Sopenharmony_ci &f_midi2_ep_opts_attr_family, 244762306a36Sopenharmony_ci &f_midi2_ep_opts_attr_model, 244862306a36Sopenharmony_ci &f_midi2_ep_opts_attr_sw_revision, 244962306a36Sopenharmony_ci NULL, 245062306a36Sopenharmony_ci}; 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_cistatic void f_midi2_ep_opts_release(struct config_item *item) 245362306a36Sopenharmony_ci{ 245462306a36Sopenharmony_ci struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci kfree(opts->info.ep_name); 245762306a36Sopenharmony_ci kfree(opts->info.product_id); 245862306a36Sopenharmony_ci kfree(opts); 245962306a36Sopenharmony_ci} 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_cistatic struct configfs_item_operations f_midi2_ep_item_ops = { 246262306a36Sopenharmony_ci .release = f_midi2_ep_opts_release, 246362306a36Sopenharmony_ci}; 246462306a36Sopenharmony_ci 246562306a36Sopenharmony_cistatic struct configfs_group_operations f_midi2_ep_group_ops = { 246662306a36Sopenharmony_ci .make_group = f_midi2_opts_block_make, 246762306a36Sopenharmony_ci .drop_item = f_midi2_opts_block_drop, 246862306a36Sopenharmony_ci}; 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_cistatic const struct config_item_type f_midi2_ep_type = { 247162306a36Sopenharmony_ci .ct_item_ops = &f_midi2_ep_item_ops, 247262306a36Sopenharmony_ci .ct_group_ops = &f_midi2_ep_group_ops, 247362306a36Sopenharmony_ci .ct_attrs = f_midi2_ep_attrs, 247462306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 247562306a36Sopenharmony_ci}; 247662306a36Sopenharmony_ci 247762306a36Sopenharmony_ci/* create a f_midi2_ep_opts instance */ 247862306a36Sopenharmony_cistatic int f_midi2_ep_opts_create(struct f_midi2_opts *opts, 247962306a36Sopenharmony_ci unsigned int index, 248062306a36Sopenharmony_ci struct f_midi2_ep_opts **ep_p) 248162306a36Sopenharmony_ci{ 248262306a36Sopenharmony_ci struct f_midi2_ep_opts *ep_opts; 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_ci ep_opts = kzalloc(sizeof(*ep_opts), GFP_KERNEL); 248562306a36Sopenharmony_ci if (!ep_opts) 248662306a36Sopenharmony_ci return -ENOMEM; 248762306a36Sopenharmony_ci 248862306a36Sopenharmony_ci ep_opts->opts = opts; 248962306a36Sopenharmony_ci ep_opts->index = index; 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ci /* set up the default values */ 249262306a36Sopenharmony_ci ep_opts->info.protocol = 2; 249362306a36Sopenharmony_ci ep_opts->info.protocol_caps = 3; 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_ci opts->eps[index] = ep_opts; 249662306a36Sopenharmony_ci *ep_p = ep_opts; 249762306a36Sopenharmony_ci return 0; 249862306a36Sopenharmony_ci} 249962306a36Sopenharmony_ci 250062306a36Sopenharmony_ci/* make_group callback for an EP */ 250162306a36Sopenharmony_cistatic struct config_group * 250262306a36Sopenharmony_cif_midi2_opts_ep_make(struct config_group *group, const char *name) 250362306a36Sopenharmony_ci{ 250462306a36Sopenharmony_ci struct f_midi2_opts *opts; 250562306a36Sopenharmony_ci struct f_midi2_ep_opts *ep_opts; 250662306a36Sopenharmony_ci unsigned int index; 250762306a36Sopenharmony_ci int ret; 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci if (strncmp(name, "ep.", 3)) 251062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 251162306a36Sopenharmony_ci ret = kstrtouint(name + 3, 10, &index); 251262306a36Sopenharmony_ci if (ret) 251362306a36Sopenharmony_ci return ERR_PTR(ret); 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci opts = to_f_midi2_opts(&group->cg_item); 251662306a36Sopenharmony_ci if (index >= MAX_UMP_EPS) 251762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 251862306a36Sopenharmony_ci if (opts->eps[index]) 251962306a36Sopenharmony_ci return ERR_PTR(-EBUSY); 252062306a36Sopenharmony_ci ret = f_midi2_ep_opts_create(opts, index, &ep_opts); 252162306a36Sopenharmony_ci if (ret) 252262306a36Sopenharmony_ci return ERR_PTR(ret); 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci config_group_init_type_name(&ep_opts->group, name, &f_midi2_ep_type); 252562306a36Sopenharmony_ci return &ep_opts->group; 252662306a36Sopenharmony_ci} 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci/* drop_item callback for an EP */ 252962306a36Sopenharmony_cistatic void 253062306a36Sopenharmony_cif_midi2_opts_ep_drop(struct config_group *group, struct config_item *item) 253162306a36Sopenharmony_ci{ 253262306a36Sopenharmony_ci struct f_midi2_ep_opts *ep_opts = to_f_midi2_ep_opts(item); 253362306a36Sopenharmony_ci 253462306a36Sopenharmony_ci mutex_lock(&ep_opts->opts->lock); 253562306a36Sopenharmony_ci ep_opts->opts->eps[ep_opts->index] = NULL; 253662306a36Sopenharmony_ci mutex_unlock(&ep_opts->opts->lock); 253762306a36Sopenharmony_ci config_item_put(item); 253862306a36Sopenharmony_ci} 253962306a36Sopenharmony_ci 254062306a36Sopenharmony_ci/* 254162306a36Sopenharmony_ci * Definitions for card config 254262306a36Sopenharmony_ci */ 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci/* define a bool option for card */ 254562306a36Sopenharmony_ci#define F_MIDI2_BOOL_OPT(name) \ 254662306a36Sopenharmony_cistatic ssize_t f_midi2_opts_##name##_show(struct config_item *item, \ 254762306a36Sopenharmony_ci char *page) \ 254862306a36Sopenharmony_ci{ \ 254962306a36Sopenharmony_ci struct f_midi2_opts *opts = to_f_midi2_opts(item); \ 255062306a36Sopenharmony_ci return f_midi2_opts_uint_show(opts, opts->info.name, \ 255162306a36Sopenharmony_ci "%u\n", page); \ 255262306a36Sopenharmony_ci} \ 255362306a36Sopenharmony_ci \ 255462306a36Sopenharmony_cistatic ssize_t f_midi2_opts_##name##_store(struct config_item *item, \ 255562306a36Sopenharmony_ci const char *page, size_t len) \ 255662306a36Sopenharmony_ci{ \ 255762306a36Sopenharmony_ci struct f_midi2_opts *opts = to_f_midi2_opts(item); \ 255862306a36Sopenharmony_ci return f_midi2_opts_bool_store(opts, &opts->info.name, \ 255962306a36Sopenharmony_ci page, len); \ 256062306a36Sopenharmony_ci} \ 256162306a36Sopenharmony_ci \ 256262306a36Sopenharmony_ciCONFIGFS_ATTR(f_midi2_opts_, name) 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ciF_MIDI2_BOOL_OPT(process_ump); 256562306a36Sopenharmony_ciF_MIDI2_BOOL_OPT(static_block); 256662306a36Sopenharmony_ci 256762306a36Sopenharmony_cistatic ssize_t f_midi2_opts_iface_name_show(struct config_item *item, 256862306a36Sopenharmony_ci char *page) 256962306a36Sopenharmony_ci{ 257062306a36Sopenharmony_ci struct f_midi2_opts *opts = to_f_midi2_opts(item); 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_ci return f_midi2_opts_str_show(opts, opts->info.iface_name, page); 257362306a36Sopenharmony_ci} 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_cistatic ssize_t f_midi2_opts_iface_name_store(struct config_item *item, 257662306a36Sopenharmony_ci const char *page, size_t len) 257762306a36Sopenharmony_ci{ 257862306a36Sopenharmony_ci struct f_midi2_opts *opts = to_f_midi2_opts(item); 257962306a36Sopenharmony_ci 258062306a36Sopenharmony_ci return f_midi2_opts_str_store(opts, &opts->info.iface_name, 128, 258162306a36Sopenharmony_ci page, len); 258262306a36Sopenharmony_ci} 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ciCONFIGFS_ATTR(f_midi2_opts_, iface_name); 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_cistatic struct configfs_attribute *f_midi2_attrs[] = { 258762306a36Sopenharmony_ci &f_midi2_opts_attr_process_ump, 258862306a36Sopenharmony_ci &f_midi2_opts_attr_static_block, 258962306a36Sopenharmony_ci &f_midi2_opts_attr_iface_name, 259062306a36Sopenharmony_ci NULL 259162306a36Sopenharmony_ci}; 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_cistatic void f_midi2_opts_release(struct config_item *item) 259462306a36Sopenharmony_ci{ 259562306a36Sopenharmony_ci struct f_midi2_opts *opts = to_f_midi2_opts(item); 259662306a36Sopenharmony_ci 259762306a36Sopenharmony_ci usb_put_function_instance(&opts->func_inst); 259862306a36Sopenharmony_ci} 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_cistatic struct configfs_item_operations f_midi2_item_ops = { 260162306a36Sopenharmony_ci .release = f_midi2_opts_release, 260262306a36Sopenharmony_ci}; 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_cistatic struct configfs_group_operations f_midi2_group_ops = { 260562306a36Sopenharmony_ci .make_group = f_midi2_opts_ep_make, 260662306a36Sopenharmony_ci .drop_item = f_midi2_opts_ep_drop, 260762306a36Sopenharmony_ci}; 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_cistatic const struct config_item_type f_midi2_func_type = { 261062306a36Sopenharmony_ci .ct_item_ops = &f_midi2_item_ops, 261162306a36Sopenharmony_ci .ct_group_ops = &f_midi2_group_ops, 261262306a36Sopenharmony_ci .ct_attrs = f_midi2_attrs, 261362306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 261462306a36Sopenharmony_ci}; 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_cistatic void f_midi2_free_inst(struct usb_function_instance *f) 261762306a36Sopenharmony_ci{ 261862306a36Sopenharmony_ci struct f_midi2_opts *opts; 261962306a36Sopenharmony_ci 262062306a36Sopenharmony_ci opts = container_of(f, struct f_midi2_opts, func_inst); 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_ci kfree(opts->info.iface_name); 262362306a36Sopenharmony_ci kfree(opts); 262462306a36Sopenharmony_ci} 262562306a36Sopenharmony_ci 262662306a36Sopenharmony_ci/* gadget alloc_inst */ 262762306a36Sopenharmony_cistatic struct usb_function_instance *f_midi2_alloc_inst(void) 262862306a36Sopenharmony_ci{ 262962306a36Sopenharmony_ci struct f_midi2_opts *opts; 263062306a36Sopenharmony_ci struct f_midi2_ep_opts *ep_opts; 263162306a36Sopenharmony_ci struct f_midi2_block_opts *block_opts; 263262306a36Sopenharmony_ci int ret; 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci opts = kzalloc(sizeof(*opts), GFP_KERNEL); 263562306a36Sopenharmony_ci if (!opts) 263662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_ci mutex_init(&opts->lock); 263962306a36Sopenharmony_ci opts->func_inst.free_func_inst = f_midi2_free_inst; 264062306a36Sopenharmony_ci opts->info.process_ump = true; 264162306a36Sopenharmony_ci opts->info.static_block = true; 264262306a36Sopenharmony_ci opts->info.num_reqs = 32; 264362306a36Sopenharmony_ci opts->info.req_buf_size = 512; 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_ci /* create the default ep */ 264662306a36Sopenharmony_ci ret = f_midi2_ep_opts_create(opts, 0, &ep_opts); 264762306a36Sopenharmony_ci if (ret) { 264862306a36Sopenharmony_ci kfree(opts); 264962306a36Sopenharmony_ci return ERR_PTR(ret); 265062306a36Sopenharmony_ci } 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci /* create the default block */ 265362306a36Sopenharmony_ci ret = f_midi2_block_opts_create(ep_opts, 0, &block_opts); 265462306a36Sopenharmony_ci if (ret) { 265562306a36Sopenharmony_ci kfree(ep_opts); 265662306a36Sopenharmony_ci kfree(opts); 265762306a36Sopenharmony_ci return ERR_PTR(ret); 265862306a36Sopenharmony_ci } 265962306a36Sopenharmony_ci 266062306a36Sopenharmony_ci /* set up the default MIDI1 (that is mandatory) */ 266162306a36Sopenharmony_ci block_opts->info.midi1_num_groups = 1; 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_ci config_group_init_type_name(&opts->func_inst.group, "", 266462306a36Sopenharmony_ci &f_midi2_func_type); 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_ci config_group_init_type_name(&ep_opts->group, "ep.0", 266762306a36Sopenharmony_ci &f_midi2_ep_type); 266862306a36Sopenharmony_ci configfs_add_default_group(&ep_opts->group, &opts->func_inst.group); 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_ci config_group_init_type_name(&block_opts->group, "block.0", 267162306a36Sopenharmony_ci &f_midi2_block_type); 267262306a36Sopenharmony_ci configfs_add_default_group(&block_opts->group, &ep_opts->group); 267362306a36Sopenharmony_ci 267462306a36Sopenharmony_ci return &opts->func_inst; 267562306a36Sopenharmony_ci} 267662306a36Sopenharmony_ci 267762306a36Sopenharmony_cistatic void do_f_midi2_free(struct f_midi2 *midi2, struct f_midi2_opts *opts) 267862306a36Sopenharmony_ci{ 267962306a36Sopenharmony_ci mutex_lock(&opts->lock); 268062306a36Sopenharmony_ci --opts->refcnt; 268162306a36Sopenharmony_ci mutex_unlock(&opts->lock); 268262306a36Sopenharmony_ci kfree(midi2->string_defs); 268362306a36Sopenharmony_ci kfree(midi2); 268462306a36Sopenharmony_ci} 268562306a36Sopenharmony_ci 268662306a36Sopenharmony_cistatic void f_midi2_free(struct usb_function *f) 268762306a36Sopenharmony_ci{ 268862306a36Sopenharmony_ci do_f_midi2_free(func_to_midi2(f), 268962306a36Sopenharmony_ci container_of(f->fi, struct f_midi2_opts, func_inst)); 269062306a36Sopenharmony_ci} 269162306a36Sopenharmony_ci 269262306a36Sopenharmony_ci/* verify the parameters set up via configfs; 269362306a36Sopenharmony_ci * return the number of EPs or a negative error 269462306a36Sopenharmony_ci */ 269562306a36Sopenharmony_cistatic int verify_parameters(struct f_midi2_opts *opts) 269662306a36Sopenharmony_ci{ 269762306a36Sopenharmony_ci int i, j, num_eps, num_blks; 269862306a36Sopenharmony_ci struct f_midi2_ep_info *ep; 269962306a36Sopenharmony_ci struct f_midi2_block_info *bp; 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_ci for (num_eps = 0; num_eps < MAX_UMP_EPS && opts->eps[num_eps]; 270262306a36Sopenharmony_ci num_eps++) 270362306a36Sopenharmony_ci ; 270462306a36Sopenharmony_ci if (!num_eps) { 270562306a36Sopenharmony_ci pr_err("f_midi2: No EP is defined\n"); 270662306a36Sopenharmony_ci return -EINVAL; 270762306a36Sopenharmony_ci } 270862306a36Sopenharmony_ci 270962306a36Sopenharmony_ci num_blks = 0; 271062306a36Sopenharmony_ci for (i = 0; i < num_eps; i++) { 271162306a36Sopenharmony_ci ep = &opts->eps[i]->info; 271262306a36Sopenharmony_ci if (!(ep->protocol_caps & ep->protocol)) { 271362306a36Sopenharmony_ci pr_err("f_midi2: Invalid protocol 0x%x (caps 0x%x) for EP %d\n", 271462306a36Sopenharmony_ci ep->protocol, ep->protocol_caps, i); 271562306a36Sopenharmony_ci return -EINVAL; 271662306a36Sopenharmony_ci } 271762306a36Sopenharmony_ci 271862306a36Sopenharmony_ci for (j = 0; j < SNDRV_UMP_MAX_BLOCKS && opts->eps[i]->blks[j]; 271962306a36Sopenharmony_ci j++, num_blks++) { 272062306a36Sopenharmony_ci bp = &opts->eps[i]->blks[j]->info; 272162306a36Sopenharmony_ci if (bp->first_group + bp->num_groups > SNDRV_UMP_MAX_GROUPS) { 272262306a36Sopenharmony_ci pr_err("f_midi2: Invalid group definitions for block %d:%d\n", 272362306a36Sopenharmony_ci i, j); 272462306a36Sopenharmony_ci return -EINVAL; 272562306a36Sopenharmony_ci } 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci if (bp->midi1_num_groups) { 272862306a36Sopenharmony_ci if (bp->midi1_first_group < bp->first_group || 272962306a36Sopenharmony_ci bp->midi1_first_group + bp->midi1_num_groups > 273062306a36Sopenharmony_ci bp->first_group + bp->num_groups) { 273162306a36Sopenharmony_ci pr_err("f_midi2: Invalid MIDI1 group definitions for block %d:%d\n", 273262306a36Sopenharmony_ci i, j); 273362306a36Sopenharmony_ci return -EINVAL; 273462306a36Sopenharmony_ci } 273562306a36Sopenharmony_ci } 273662306a36Sopenharmony_ci } 273762306a36Sopenharmony_ci } 273862306a36Sopenharmony_ci if (!num_blks) { 273962306a36Sopenharmony_ci pr_err("f_midi2: No block is defined\n"); 274062306a36Sopenharmony_ci return -EINVAL; 274162306a36Sopenharmony_ci } 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_ci return num_eps; 274462306a36Sopenharmony_ci} 274562306a36Sopenharmony_ci 274662306a36Sopenharmony_ci/* fill mapping between MIDI 1.0 cable and UMP EP/group */ 274762306a36Sopenharmony_cistatic void fill_midi1_cable_mapping(struct f_midi2 *midi2, 274862306a36Sopenharmony_ci struct f_midi2_ep *ep, 274962306a36Sopenharmony_ci int blk) 275062306a36Sopenharmony_ci{ 275162306a36Sopenharmony_ci const struct f_midi2_block_info *binfo = &ep->blks[blk].info; 275262306a36Sopenharmony_ci struct midi1_cable_mapping *map; 275362306a36Sopenharmony_ci int i, group; 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci if (!binfo->midi1_num_groups) 275662306a36Sopenharmony_ci return; 275762306a36Sopenharmony_ci if (binfo->direction != SNDRV_UMP_DIR_OUTPUT) { 275862306a36Sopenharmony_ci group = binfo->midi1_first_group; 275962306a36Sopenharmony_ci map = midi2->in_cable_mapping + midi2->num_midi1_in; 276062306a36Sopenharmony_ci for (i = 0; i < binfo->midi1_num_groups; i++, group++, map++) { 276162306a36Sopenharmony_ci if (midi2->num_midi1_in >= MAX_CABLES) 276262306a36Sopenharmony_ci break; 276362306a36Sopenharmony_ci map->ep = ep; 276462306a36Sopenharmony_ci map->block = blk; 276562306a36Sopenharmony_ci map->group = group; 276662306a36Sopenharmony_ci midi2->num_midi1_in++; 276762306a36Sopenharmony_ci /* store 1-based cable number */ 276862306a36Sopenharmony_ci ep->in_group_to_cable[group] = midi2->num_midi1_in; 276962306a36Sopenharmony_ci } 277062306a36Sopenharmony_ci } 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_ci if (binfo->direction != SNDRV_UMP_DIR_INPUT) { 277362306a36Sopenharmony_ci group = binfo->midi1_first_group; 277462306a36Sopenharmony_ci map = midi2->out_cable_mapping + midi2->num_midi1_out; 277562306a36Sopenharmony_ci for (i = 0; i < binfo->midi1_num_groups; i++, group++, map++) { 277662306a36Sopenharmony_ci if (midi2->num_midi1_out >= MAX_CABLES) 277762306a36Sopenharmony_ci break; 277862306a36Sopenharmony_ci map->ep = ep; 277962306a36Sopenharmony_ci map->block = blk; 278062306a36Sopenharmony_ci map->group = group; 278162306a36Sopenharmony_ci midi2->num_midi1_out++; 278262306a36Sopenharmony_ci } 278362306a36Sopenharmony_ci } 278462306a36Sopenharmony_ci} 278562306a36Sopenharmony_ci 278662306a36Sopenharmony_ci/* gadget alloc callback */ 278762306a36Sopenharmony_cistatic struct usb_function *f_midi2_alloc(struct usb_function_instance *fi) 278862306a36Sopenharmony_ci{ 278962306a36Sopenharmony_ci struct f_midi2 *midi2; 279062306a36Sopenharmony_ci struct f_midi2_opts *opts; 279162306a36Sopenharmony_ci struct f_midi2_ep *ep; 279262306a36Sopenharmony_ci struct f_midi2_block *bp; 279362306a36Sopenharmony_ci int i, num_eps, blk; 279462306a36Sopenharmony_ci 279562306a36Sopenharmony_ci midi2 = kzalloc(sizeof(*midi2), GFP_KERNEL); 279662306a36Sopenharmony_ci if (!midi2) 279762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci opts = container_of(fi, struct f_midi2_opts, func_inst); 280062306a36Sopenharmony_ci mutex_lock(&opts->lock); 280162306a36Sopenharmony_ci num_eps = verify_parameters(opts); 280262306a36Sopenharmony_ci if (num_eps < 0) { 280362306a36Sopenharmony_ci mutex_unlock(&opts->lock); 280462306a36Sopenharmony_ci kfree(midi2); 280562306a36Sopenharmony_ci return ERR_PTR(num_eps); 280662306a36Sopenharmony_ci } 280762306a36Sopenharmony_ci ++opts->refcnt; 280862306a36Sopenharmony_ci mutex_unlock(&opts->lock); 280962306a36Sopenharmony_ci 281062306a36Sopenharmony_ci spin_lock_init(&midi2->queue_lock); 281162306a36Sopenharmony_ci 281262306a36Sopenharmony_ci midi2->func.name = "midi2_func"; 281362306a36Sopenharmony_ci midi2->func.bind = f_midi2_bind; 281462306a36Sopenharmony_ci midi2->func.unbind = f_midi2_unbind; 281562306a36Sopenharmony_ci midi2->func.get_alt = f_midi2_get_alt; 281662306a36Sopenharmony_ci midi2->func.set_alt = f_midi2_set_alt; 281762306a36Sopenharmony_ci midi2->func.setup = f_midi2_setup; 281862306a36Sopenharmony_ci midi2->func.disable = f_midi2_disable; 281962306a36Sopenharmony_ci midi2->func.free_func = f_midi2_free; 282062306a36Sopenharmony_ci 282162306a36Sopenharmony_ci midi2->info = opts->info; 282262306a36Sopenharmony_ci midi2->num_eps = num_eps; 282362306a36Sopenharmony_ci 282462306a36Sopenharmony_ci for (i = 0; i < num_eps; i++) { 282562306a36Sopenharmony_ci ep = &midi2->midi2_eps[i]; 282662306a36Sopenharmony_ci ep->info = opts->eps[i]->info; 282762306a36Sopenharmony_ci ep->card = midi2; 282862306a36Sopenharmony_ci for (blk = 0; blk < SNDRV_UMP_MAX_BLOCKS && 282962306a36Sopenharmony_ci opts->eps[i]->blks[blk]; blk++) { 283062306a36Sopenharmony_ci bp = &ep->blks[blk]; 283162306a36Sopenharmony_ci ep->num_blks++; 283262306a36Sopenharmony_ci bp->info = opts->eps[i]->blks[blk]->info; 283362306a36Sopenharmony_ci bp->gtb_id = ++midi2->total_blocks; 283462306a36Sopenharmony_ci } 283562306a36Sopenharmony_ci } 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci midi2->string_defs = kcalloc(midi2->total_blocks + 1, 283862306a36Sopenharmony_ci sizeof(*midi2->string_defs), GFP_KERNEL); 283962306a36Sopenharmony_ci if (!midi2->string_defs) { 284062306a36Sopenharmony_ci do_f_midi2_free(midi2, opts); 284162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 284262306a36Sopenharmony_ci } 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_ci if (opts->info.iface_name && *opts->info.iface_name) 284562306a36Sopenharmony_ci midi2->string_defs[STR_IFACE].s = opts->info.iface_name; 284662306a36Sopenharmony_ci else 284762306a36Sopenharmony_ci midi2->string_defs[STR_IFACE].s = ump_ep_name(&midi2->midi2_eps[0]); 284862306a36Sopenharmony_ci 284962306a36Sopenharmony_ci for (i = 0; i < midi2->num_eps; i++) { 285062306a36Sopenharmony_ci ep = &midi2->midi2_eps[i]; 285162306a36Sopenharmony_ci for (blk = 0; blk < ep->num_blks; blk++) { 285262306a36Sopenharmony_ci bp = &ep->blks[blk]; 285362306a36Sopenharmony_ci midi2->string_defs[gtb_to_str_id(bp->gtb_id)].s = 285462306a36Sopenharmony_ci ump_fb_name(&bp->info); 285562306a36Sopenharmony_ci 285662306a36Sopenharmony_ci fill_midi1_cable_mapping(midi2, ep, blk); 285762306a36Sopenharmony_ci } 285862306a36Sopenharmony_ci } 285962306a36Sopenharmony_ci 286062306a36Sopenharmony_ci if (!midi2->num_midi1_in && !midi2->num_midi1_out) { 286162306a36Sopenharmony_ci pr_err("f_midi2: MIDI1 definition is missing\n"); 286262306a36Sopenharmony_ci do_f_midi2_free(midi2, opts); 286362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 286462306a36Sopenharmony_ci } 286562306a36Sopenharmony_ci 286662306a36Sopenharmony_ci return &midi2->func; 286762306a36Sopenharmony_ci} 286862306a36Sopenharmony_ci 286962306a36Sopenharmony_ciDECLARE_USB_FUNCTION_INIT(midi2, f_midi2_alloc_inst, f_midi2_alloc); 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2872