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