162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * f_midi.c -- USB MIDI class function driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2006 Thumtronics Pty Ltd.
662306a36Sopenharmony_ci * Developed for Thumtronics by Grey Innovation
762306a36Sopenharmony_ci * Ben Williamson <ben.williamson@greyinnovation.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Rewritten for the composite framework
1062306a36Sopenharmony_ci *   Copyright (C) 2011 Daniel Mack <zonque@gmail.com>
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Based on drivers/usb/gadget/f_audio.c,
1362306a36Sopenharmony_ci *   Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
1462306a36Sopenharmony_ci *   Copyright (C) 2008 Analog Devices, Inc
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * and drivers/usb/gadget/midi.c,
1762306a36Sopenharmony_ci *   Copyright (C) 2006 Thumtronics Pty Ltd.
1862306a36Sopenharmony_ci *   Ben Williamson <ben.williamson@greyinnovation.com>
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/kernel.h>
2262306a36Sopenharmony_ci#include <linux/module.h>
2362306a36Sopenharmony_ci#include <linux/slab.h>
2462306a36Sopenharmony_ci#include <linux/device.h>
2562306a36Sopenharmony_ci#include <linux/kfifo.h>
2662306a36Sopenharmony_ci#include <linux/spinlock.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <sound/core.h>
2962306a36Sopenharmony_ci#include <sound/initval.h>
3062306a36Sopenharmony_ci#include <sound/rawmidi.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include <linux/usb/ch9.h>
3362306a36Sopenharmony_ci#include <linux/usb/gadget.h>
3462306a36Sopenharmony_ci#include <linux/usb/audio.h>
3562306a36Sopenharmony_ci#include <linux/usb/midi.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include "u_f.h"
3862306a36Sopenharmony_ci#include "u_midi.h"
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ciMODULE_AUTHOR("Ben Williamson");
4162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic const char f_midi_shortname[] = "f_midi";
4462306a36Sopenharmony_cistatic const char f_midi_longname[] = "MIDI Gadget";
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/*
4762306a36Sopenharmony_ci * We can only handle 16 cables on one single endpoint, as cable numbers are
4862306a36Sopenharmony_ci * stored in 4-bit fields. And as the interface currently only holds one
4962306a36Sopenharmony_ci * single endpoint, this is the maximum number of ports we can allow.
5062306a36Sopenharmony_ci */
5162306a36Sopenharmony_ci#define MAX_PORTS 16
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* MIDI message states */
5462306a36Sopenharmony_cienum {
5562306a36Sopenharmony_ci	STATE_INITIAL = 0,	/* pseudo state */
5662306a36Sopenharmony_ci	STATE_1PARAM,
5762306a36Sopenharmony_ci	STATE_2PARAM_1,
5862306a36Sopenharmony_ci	STATE_2PARAM_2,
5962306a36Sopenharmony_ci	STATE_SYSEX_0,
6062306a36Sopenharmony_ci	STATE_SYSEX_1,
6162306a36Sopenharmony_ci	STATE_SYSEX_2,
6262306a36Sopenharmony_ci	STATE_REAL_TIME,
6362306a36Sopenharmony_ci	STATE_FINISHED,		/* pseudo state */
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/*
6762306a36Sopenharmony_ci * This is a gadget, and the IN/OUT naming is from the host's perspective.
6862306a36Sopenharmony_ci * USB -> OUT endpoint -> rawmidi
6962306a36Sopenharmony_ci * USB <- IN endpoint  <- rawmidi
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_cistruct gmidi_in_port {
7262306a36Sopenharmony_ci	struct snd_rawmidi_substream *substream;
7362306a36Sopenharmony_ci	int active;
7462306a36Sopenharmony_ci	uint8_t cable;
7562306a36Sopenharmony_ci	uint8_t state;
7662306a36Sopenharmony_ci	uint8_t data[2];
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistruct f_midi {
8062306a36Sopenharmony_ci	struct usb_function	func;
8162306a36Sopenharmony_ci	struct usb_gadget	*gadget;
8262306a36Sopenharmony_ci	struct usb_ep		*in_ep, *out_ep;
8362306a36Sopenharmony_ci	struct snd_card		*card;
8462306a36Sopenharmony_ci	struct snd_rawmidi	*rmidi;
8562306a36Sopenharmony_ci	u8			ms_id;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	struct snd_rawmidi_substream *out_substream[MAX_PORTS];
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	unsigned long		out_triggered;
9062306a36Sopenharmony_ci	struct work_struct	work;
9162306a36Sopenharmony_ci	unsigned int in_ports;
9262306a36Sopenharmony_ci	unsigned int out_ports;
9362306a36Sopenharmony_ci	int index;
9462306a36Sopenharmony_ci	char *id;
9562306a36Sopenharmony_ci	unsigned int buflen, qlen;
9662306a36Sopenharmony_ci	/* This fifo is used as a buffer ring for pre-allocated IN usb_requests */
9762306a36Sopenharmony_ci	DECLARE_KFIFO_PTR(in_req_fifo, struct usb_request *);
9862306a36Sopenharmony_ci	spinlock_t transmit_lock;
9962306a36Sopenharmony_ci	unsigned int in_last_port;
10062306a36Sopenharmony_ci	unsigned char free_ref;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	struct gmidi_in_port	in_ports_array[/* in_ports */];
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic inline struct f_midi *func_to_midi(struct usb_function *f)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	return container_of(f, struct f_midi, func);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic void f_midi_transmit(struct f_midi *midi);
11162306a36Sopenharmony_cistatic void f_midi_rmidi_free(struct snd_rawmidi *rmidi);
11262306a36Sopenharmony_cistatic void f_midi_free_inst(struct usb_function_instance *f);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ciDECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
11562306a36Sopenharmony_ciDECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
11662306a36Sopenharmony_ciDECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/* B.3.1  Standard AC Interface Descriptor */
11962306a36Sopenharmony_cistatic struct usb_interface_descriptor ac_interface_desc = {
12062306a36Sopenharmony_ci	.bLength =		USB_DT_INTERFACE_SIZE,
12162306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_INTERFACE,
12262306a36Sopenharmony_ci	/* .bInterfaceNumber =	DYNAMIC */
12362306a36Sopenharmony_ci	/* .bNumEndpoints =	DYNAMIC */
12462306a36Sopenharmony_ci	.bInterfaceClass =	USB_CLASS_AUDIO,
12562306a36Sopenharmony_ci	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOCONTROL,
12662306a36Sopenharmony_ci	/* .iInterface =	DYNAMIC */
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/* B.3.2  Class-Specific AC Interface Descriptor */
13062306a36Sopenharmony_cistatic struct uac1_ac_header_descriptor_1 ac_header_desc = {
13162306a36Sopenharmony_ci	.bLength =		UAC_DT_AC_HEADER_SIZE(1),
13262306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_INTERFACE,
13362306a36Sopenharmony_ci	.bDescriptorSubtype =	USB_MS_HEADER,
13462306a36Sopenharmony_ci	.bcdADC =		cpu_to_le16(0x0100),
13562306a36Sopenharmony_ci	.wTotalLength =		cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)),
13662306a36Sopenharmony_ci	.bInCollection =	1,
13762306a36Sopenharmony_ci	/* .baInterfaceNr =	DYNAMIC */
13862306a36Sopenharmony_ci};
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/* B.4.1  Standard MS Interface Descriptor */
14162306a36Sopenharmony_cistatic struct usb_interface_descriptor ms_interface_desc = {
14262306a36Sopenharmony_ci	.bLength =		USB_DT_INTERFACE_SIZE,
14362306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_INTERFACE,
14462306a36Sopenharmony_ci	/* .bInterfaceNumber =	DYNAMIC */
14562306a36Sopenharmony_ci	.bNumEndpoints =	2,
14662306a36Sopenharmony_ci	.bInterfaceClass =	USB_CLASS_AUDIO,
14762306a36Sopenharmony_ci	.bInterfaceSubClass =	USB_SUBCLASS_MIDISTREAMING,
14862306a36Sopenharmony_ci	/* .iInterface =	DYNAMIC */
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/* B.4.2  Class-Specific MS Interface Descriptor */
15262306a36Sopenharmony_cistatic struct usb_ms_header_descriptor ms_header_desc = {
15362306a36Sopenharmony_ci	.bLength =		USB_DT_MS_HEADER_SIZE,
15462306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_INTERFACE,
15562306a36Sopenharmony_ci	.bDescriptorSubtype =	USB_MS_HEADER,
15662306a36Sopenharmony_ci	.bcdMSC =		cpu_to_le16(0x0100),
15762306a36Sopenharmony_ci	/* .wTotalLength =	DYNAMIC */
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci/* B.5.1  Standard Bulk OUT Endpoint Descriptor */
16162306a36Sopenharmony_cistatic struct usb_endpoint_descriptor bulk_out_desc = {
16262306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
16362306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
16462306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_OUT,
16562306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor bulk_out_ss_comp_desc = {
16962306a36Sopenharmony_ci	.bLength                = sizeof(bulk_out_ss_comp_desc),
17062306a36Sopenharmony_ci	.bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
17162306a36Sopenharmony_ci	/* .bMaxBurst           = 0, */
17262306a36Sopenharmony_ci	/* .bmAttributes        = 0, */
17362306a36Sopenharmony_ci};
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/* B.5.2  Class-specific MS Bulk OUT Endpoint Descriptor */
17662306a36Sopenharmony_cistatic struct usb_ms_endpoint_descriptor_16 ms_out_desc = {
17762306a36Sopenharmony_ci	/* .bLength =		DYNAMIC */
17862306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_ENDPOINT,
17962306a36Sopenharmony_ci	.bDescriptorSubtype =	USB_MS_GENERAL,
18062306a36Sopenharmony_ci	/* .bNumEmbMIDIJack =	DYNAMIC */
18162306a36Sopenharmony_ci	/* .baAssocJackID =	DYNAMIC */
18262306a36Sopenharmony_ci};
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci/* B.6.1  Standard Bulk IN Endpoint Descriptor */
18562306a36Sopenharmony_cistatic struct usb_endpoint_descriptor bulk_in_desc = {
18662306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
18762306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
18862306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
18962306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
19062306a36Sopenharmony_ci};
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor bulk_in_ss_comp_desc = {
19362306a36Sopenharmony_ci	.bLength                = sizeof(bulk_in_ss_comp_desc),
19462306a36Sopenharmony_ci	.bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
19562306a36Sopenharmony_ci	/* .bMaxBurst           = 0, */
19662306a36Sopenharmony_ci	/* .bmAttributes        = 0, */
19762306a36Sopenharmony_ci};
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci/* B.6.2  Class-specific MS Bulk IN Endpoint Descriptor */
20062306a36Sopenharmony_cistatic struct usb_ms_endpoint_descriptor_16 ms_in_desc = {
20162306a36Sopenharmony_ci	/* .bLength =		DYNAMIC */
20262306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_ENDPOINT,
20362306a36Sopenharmony_ci	.bDescriptorSubtype =	USB_MS_GENERAL,
20462306a36Sopenharmony_ci	/* .bNumEmbMIDIJack =	DYNAMIC */
20562306a36Sopenharmony_ci	/* .baAssocJackID =	DYNAMIC */
20662306a36Sopenharmony_ci};
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci/* string IDs are assigned dynamically */
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci#define STRING_FUNC_IDX			0
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic struct usb_string midi_string_defs[] = {
21362306a36Sopenharmony_ci	[STRING_FUNC_IDX].s = "MIDI function",
21462306a36Sopenharmony_ci	{  } /* end of list */
21562306a36Sopenharmony_ci};
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic struct usb_gadget_strings midi_stringtab = {
21862306a36Sopenharmony_ci	.language	= 0x0409,	/* en-us */
21962306a36Sopenharmony_ci	.strings	= midi_string_defs,
22062306a36Sopenharmony_ci};
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic struct usb_gadget_strings *midi_strings[] = {
22362306a36Sopenharmony_ci	&midi_stringtab,
22462306a36Sopenharmony_ci	NULL,
22562306a36Sopenharmony_ci};
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep,
22862306a36Sopenharmony_ci						    unsigned length)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	return alloc_ep_req(ep, length);
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic const uint8_t f_midi_cin_length[] = {
23462306a36Sopenharmony_ci	0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
23562306a36Sopenharmony_ci};
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci/*
23862306a36Sopenharmony_ci * Receives a chunk of MIDI data.
23962306a36Sopenharmony_ci */
24062306a36Sopenharmony_cistatic void f_midi_read_data(struct usb_ep *ep, int cable,
24162306a36Sopenharmony_ci			     uint8_t *data, int length)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct f_midi *midi = ep->driver_data;
24462306a36Sopenharmony_ci	struct snd_rawmidi_substream *substream = midi->out_substream[cable];
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (!substream)
24762306a36Sopenharmony_ci		/* Nobody is listening - throw it on the floor. */
24862306a36Sopenharmony_ci		return;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	if (!test_bit(cable, &midi->out_triggered))
25162306a36Sopenharmony_ci		return;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	snd_rawmidi_receive(substream, data, length);
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic void f_midi_handle_out_data(struct usb_ep *ep, struct usb_request *req)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	unsigned int i;
25962306a36Sopenharmony_ci	u8 *buf = req->buf;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	for (i = 0; i + 3 < req->actual; i += 4)
26262306a36Sopenharmony_ci		if (buf[i] != 0) {
26362306a36Sopenharmony_ci			int cable = buf[i] >> 4;
26462306a36Sopenharmony_ci			int length = f_midi_cin_length[buf[i] & 0x0f];
26562306a36Sopenharmony_ci			f_midi_read_data(ep, cable, &buf[i + 1], length);
26662306a36Sopenharmony_ci		}
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic void
27062306a36Sopenharmony_cif_midi_complete(struct usb_ep *ep, struct usb_request *req)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct f_midi *midi = ep->driver_data;
27362306a36Sopenharmony_ci	struct usb_composite_dev *cdev = midi->func.config->cdev;
27462306a36Sopenharmony_ci	int status = req->status;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	switch (status) {
27762306a36Sopenharmony_ci	case 0:			 /* normal completion */
27862306a36Sopenharmony_ci		if (ep == midi->out_ep) {
27962306a36Sopenharmony_ci			/* We received stuff. req is queued again, below */
28062306a36Sopenharmony_ci			f_midi_handle_out_data(ep, req);
28162306a36Sopenharmony_ci		} else if (ep == midi->in_ep) {
28262306a36Sopenharmony_ci			/* Our transmit completed. See if there's more to go.
28362306a36Sopenharmony_ci			 * f_midi_transmit eats req, don't queue it again. */
28462306a36Sopenharmony_ci			req->length = 0;
28562306a36Sopenharmony_ci			f_midi_transmit(midi);
28662306a36Sopenharmony_ci			return;
28762306a36Sopenharmony_ci		}
28862306a36Sopenharmony_ci		break;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	/* this endpoint is normally active while we're configured */
29162306a36Sopenharmony_ci	case -ECONNABORTED:	/* hardware forced ep reset */
29262306a36Sopenharmony_ci	case -ECONNRESET:	/* request dequeued */
29362306a36Sopenharmony_ci	case -ESHUTDOWN:	/* disconnect from host */
29462306a36Sopenharmony_ci		VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
29562306a36Sopenharmony_ci				req->actual, req->length);
29662306a36Sopenharmony_ci		if (ep == midi->out_ep) {
29762306a36Sopenharmony_ci			f_midi_handle_out_data(ep, req);
29862306a36Sopenharmony_ci			/* We don't need to free IN requests because it's handled
29962306a36Sopenharmony_ci			 * by the midi->in_req_fifo. */
30062306a36Sopenharmony_ci			free_ep_req(ep, req);
30162306a36Sopenharmony_ci		}
30262306a36Sopenharmony_ci		return;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	case -EOVERFLOW:	/* buffer overrun on read means that
30562306a36Sopenharmony_ci				 * we didn't provide a big enough buffer.
30662306a36Sopenharmony_ci				 */
30762306a36Sopenharmony_ci	default:
30862306a36Sopenharmony_ci		DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name,
30962306a36Sopenharmony_ci				status, req->actual, req->length);
31062306a36Sopenharmony_ci		break;
31162306a36Sopenharmony_ci	case -EREMOTEIO:	/* short read */
31262306a36Sopenharmony_ci		break;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	status = usb_ep_queue(ep, req, GFP_ATOMIC);
31662306a36Sopenharmony_ci	if (status) {
31762306a36Sopenharmony_ci		ERROR(cdev, "kill %s:  resubmit %d bytes --> %d\n",
31862306a36Sopenharmony_ci				ep->name, req->length, status);
31962306a36Sopenharmony_ci		usb_ep_set_halt(ep);
32062306a36Sopenharmony_ci		/* FIXME recover later ... somehow */
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic void f_midi_drop_out_substreams(struct f_midi *midi)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	unsigned int i;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	for (i = 0; i < midi->in_ports; i++) {
32962306a36Sopenharmony_ci		struct gmidi_in_port *port = midi->in_ports_array + i;
33062306a36Sopenharmony_ci		struct snd_rawmidi_substream *substream = port->substream;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci		if (port->active && substream)
33362306a36Sopenharmony_ci			snd_rawmidi_drop_output(substream);
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic int f_midi_start_ep(struct f_midi *midi,
33862306a36Sopenharmony_ci			   struct usb_function *f,
33962306a36Sopenharmony_ci			   struct usb_ep *ep)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	int err;
34262306a36Sopenharmony_ci	struct usb_composite_dev *cdev = f->config->cdev;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	usb_ep_disable(ep);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	err = config_ep_by_speed(midi->gadget, f, ep);
34762306a36Sopenharmony_ci	if (err) {
34862306a36Sopenharmony_ci		ERROR(cdev, "can't configure %s: %d\n", ep->name, err);
34962306a36Sopenharmony_ci		return err;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	err = usb_ep_enable(ep);
35362306a36Sopenharmony_ci	if (err) {
35462306a36Sopenharmony_ci		ERROR(cdev, "can't start %s: %d\n", ep->name, err);
35562306a36Sopenharmony_ci		return err;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	ep->driver_data = midi;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	return 0;
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct f_midi *midi = func_to_midi(f);
36662306a36Sopenharmony_ci	unsigned i;
36762306a36Sopenharmony_ci	int err;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* we only set alt for MIDIStreaming interface */
37062306a36Sopenharmony_ci	if (intf != midi->ms_id)
37162306a36Sopenharmony_ci		return 0;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	err = f_midi_start_ep(midi, f, midi->in_ep);
37462306a36Sopenharmony_ci	if (err)
37562306a36Sopenharmony_ci		return err;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	err = f_midi_start_ep(midi, f, midi->out_ep);
37862306a36Sopenharmony_ci	if (err)
37962306a36Sopenharmony_ci		return err;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	/* pre-allocate write usb requests to use on f_midi_transmit. */
38262306a36Sopenharmony_ci	while (kfifo_avail(&midi->in_req_fifo)) {
38362306a36Sopenharmony_ci		struct usb_request *req =
38462306a36Sopenharmony_ci			midi_alloc_ep_req(midi->in_ep, midi->buflen);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		if (req == NULL)
38762306a36Sopenharmony_ci			return -ENOMEM;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		req->length = 0;
39062306a36Sopenharmony_ci		req->complete = f_midi_complete;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		kfifo_put(&midi->in_req_fifo, req);
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/* allocate a bunch of read buffers and queue them all at once. */
39662306a36Sopenharmony_ci	for (i = 0; i < midi->qlen && err == 0; i++) {
39762306a36Sopenharmony_ci		struct usb_request *req =
39862306a36Sopenharmony_ci			midi_alloc_ep_req(midi->out_ep, midi->buflen);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci		if (req == NULL)
40162306a36Sopenharmony_ci			return -ENOMEM;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci		req->complete = f_midi_complete;
40462306a36Sopenharmony_ci		err = usb_ep_queue(midi->out_ep, req, GFP_ATOMIC);
40562306a36Sopenharmony_ci		if (err) {
40662306a36Sopenharmony_ci			ERROR(midi, "%s: couldn't enqueue request: %d\n",
40762306a36Sopenharmony_ci				    midi->out_ep->name, err);
40862306a36Sopenharmony_ci			if (req->buf != NULL)
40962306a36Sopenharmony_ci				free_ep_req(midi->out_ep, req);
41062306a36Sopenharmony_ci			return err;
41162306a36Sopenharmony_ci		}
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	return 0;
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic void f_midi_disable(struct usb_function *f)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	struct f_midi *midi = func_to_midi(f);
42062306a36Sopenharmony_ci	struct usb_composite_dev *cdev = f->config->cdev;
42162306a36Sopenharmony_ci	struct usb_request *req = NULL;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	DBG(cdev, "disable\n");
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	/*
42662306a36Sopenharmony_ci	 * just disable endpoints, forcing completion of pending i/o.
42762306a36Sopenharmony_ci	 * all our completion handlers free their requests in this case.
42862306a36Sopenharmony_ci	 */
42962306a36Sopenharmony_ci	usb_ep_disable(midi->in_ep);
43062306a36Sopenharmony_ci	usb_ep_disable(midi->out_ep);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* release IN requests */
43362306a36Sopenharmony_ci	while (kfifo_get(&midi->in_req_fifo, &req))
43462306a36Sopenharmony_ci		free_ep_req(midi->in_ep, req);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	f_midi_drop_out_substreams(midi);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic int f_midi_snd_free(struct snd_device *device)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	return 0;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci/*
44562306a36Sopenharmony_ci * Converts MIDI commands to USB MIDI packets.
44662306a36Sopenharmony_ci */
44762306a36Sopenharmony_cistatic void f_midi_transmit_byte(struct usb_request *req,
44862306a36Sopenharmony_ci				 struct gmidi_in_port *port, uint8_t b)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	uint8_t p[4] = { port->cable << 4, 0, 0, 0 };
45162306a36Sopenharmony_ci	uint8_t next_state = STATE_INITIAL;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	switch (b) {
45462306a36Sopenharmony_ci	case 0xf8 ... 0xff:
45562306a36Sopenharmony_ci		/* System Real-Time Messages */
45662306a36Sopenharmony_ci		p[0] |= 0x0f;
45762306a36Sopenharmony_ci		p[1] = b;
45862306a36Sopenharmony_ci		next_state = port->state;
45962306a36Sopenharmony_ci		port->state = STATE_REAL_TIME;
46062306a36Sopenharmony_ci		break;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	case 0xf7:
46362306a36Sopenharmony_ci		/* End of SysEx */
46462306a36Sopenharmony_ci		switch (port->state) {
46562306a36Sopenharmony_ci		case STATE_SYSEX_0:
46662306a36Sopenharmony_ci			p[0] |= 0x05;
46762306a36Sopenharmony_ci			p[1] = 0xf7;
46862306a36Sopenharmony_ci			next_state = STATE_FINISHED;
46962306a36Sopenharmony_ci			break;
47062306a36Sopenharmony_ci		case STATE_SYSEX_1:
47162306a36Sopenharmony_ci			p[0] |= 0x06;
47262306a36Sopenharmony_ci			p[1] = port->data[0];
47362306a36Sopenharmony_ci			p[2] = 0xf7;
47462306a36Sopenharmony_ci			next_state = STATE_FINISHED;
47562306a36Sopenharmony_ci			break;
47662306a36Sopenharmony_ci		case STATE_SYSEX_2:
47762306a36Sopenharmony_ci			p[0] |= 0x07;
47862306a36Sopenharmony_ci			p[1] = port->data[0];
47962306a36Sopenharmony_ci			p[2] = port->data[1];
48062306a36Sopenharmony_ci			p[3] = 0xf7;
48162306a36Sopenharmony_ci			next_state = STATE_FINISHED;
48262306a36Sopenharmony_ci			break;
48362306a36Sopenharmony_ci		default:
48462306a36Sopenharmony_ci			/* Ignore byte */
48562306a36Sopenharmony_ci			next_state = port->state;
48662306a36Sopenharmony_ci			port->state = STATE_INITIAL;
48762306a36Sopenharmony_ci		}
48862306a36Sopenharmony_ci		break;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	case 0xf0 ... 0xf6:
49162306a36Sopenharmony_ci		/* System Common Messages */
49262306a36Sopenharmony_ci		port->data[0] = port->data[1] = 0;
49362306a36Sopenharmony_ci		port->state = STATE_INITIAL;
49462306a36Sopenharmony_ci		switch (b) {
49562306a36Sopenharmony_ci		case 0xf0:
49662306a36Sopenharmony_ci			port->data[0] = b;
49762306a36Sopenharmony_ci			port->data[1] = 0;
49862306a36Sopenharmony_ci			next_state = STATE_SYSEX_1;
49962306a36Sopenharmony_ci			break;
50062306a36Sopenharmony_ci		case 0xf1:
50162306a36Sopenharmony_ci		case 0xf3:
50262306a36Sopenharmony_ci			port->data[0] = b;
50362306a36Sopenharmony_ci			next_state = STATE_1PARAM;
50462306a36Sopenharmony_ci			break;
50562306a36Sopenharmony_ci		case 0xf2:
50662306a36Sopenharmony_ci			port->data[0] = b;
50762306a36Sopenharmony_ci			next_state = STATE_2PARAM_1;
50862306a36Sopenharmony_ci			break;
50962306a36Sopenharmony_ci		case 0xf4:
51062306a36Sopenharmony_ci		case 0xf5:
51162306a36Sopenharmony_ci			next_state = STATE_INITIAL;
51262306a36Sopenharmony_ci			break;
51362306a36Sopenharmony_ci		case 0xf6:
51462306a36Sopenharmony_ci			p[0] |= 0x05;
51562306a36Sopenharmony_ci			p[1] = 0xf6;
51662306a36Sopenharmony_ci			next_state = STATE_FINISHED;
51762306a36Sopenharmony_ci			break;
51862306a36Sopenharmony_ci		}
51962306a36Sopenharmony_ci		break;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	case 0x80 ... 0xef:
52262306a36Sopenharmony_ci		/*
52362306a36Sopenharmony_ci		 * Channel Voice Messages, Channel Mode Messages
52462306a36Sopenharmony_ci		 * and Control Change Messages.
52562306a36Sopenharmony_ci		 */
52662306a36Sopenharmony_ci		port->data[0] = b;
52762306a36Sopenharmony_ci		port->data[1] = 0;
52862306a36Sopenharmony_ci		port->state = STATE_INITIAL;
52962306a36Sopenharmony_ci		if (b >= 0xc0 && b <= 0xdf)
53062306a36Sopenharmony_ci			next_state = STATE_1PARAM;
53162306a36Sopenharmony_ci		else
53262306a36Sopenharmony_ci			next_state = STATE_2PARAM_1;
53362306a36Sopenharmony_ci		break;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	case 0x00 ... 0x7f:
53662306a36Sopenharmony_ci		/* Message parameters */
53762306a36Sopenharmony_ci		switch (port->state) {
53862306a36Sopenharmony_ci		case STATE_1PARAM:
53962306a36Sopenharmony_ci			if (port->data[0] < 0xf0)
54062306a36Sopenharmony_ci				p[0] |= port->data[0] >> 4;
54162306a36Sopenharmony_ci			else
54262306a36Sopenharmony_ci				p[0] |= 0x02;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci			p[1] = port->data[0];
54562306a36Sopenharmony_ci			p[2] = b;
54662306a36Sopenharmony_ci			/* This is to allow Running State Messages */
54762306a36Sopenharmony_ci			next_state = STATE_1PARAM;
54862306a36Sopenharmony_ci			break;
54962306a36Sopenharmony_ci		case STATE_2PARAM_1:
55062306a36Sopenharmony_ci			port->data[1] = b;
55162306a36Sopenharmony_ci			next_state = STATE_2PARAM_2;
55262306a36Sopenharmony_ci			break;
55362306a36Sopenharmony_ci		case STATE_2PARAM_2:
55462306a36Sopenharmony_ci			if (port->data[0] < 0xf0)
55562306a36Sopenharmony_ci				p[0] |= port->data[0] >> 4;
55662306a36Sopenharmony_ci			else
55762306a36Sopenharmony_ci				p[0] |= 0x03;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci			p[1] = port->data[0];
56062306a36Sopenharmony_ci			p[2] = port->data[1];
56162306a36Sopenharmony_ci			p[3] = b;
56262306a36Sopenharmony_ci			/* This is to allow Running State Messages */
56362306a36Sopenharmony_ci			next_state = STATE_2PARAM_1;
56462306a36Sopenharmony_ci			break;
56562306a36Sopenharmony_ci		case STATE_SYSEX_0:
56662306a36Sopenharmony_ci			port->data[0] = b;
56762306a36Sopenharmony_ci			next_state = STATE_SYSEX_1;
56862306a36Sopenharmony_ci			break;
56962306a36Sopenharmony_ci		case STATE_SYSEX_1:
57062306a36Sopenharmony_ci			port->data[1] = b;
57162306a36Sopenharmony_ci			next_state = STATE_SYSEX_2;
57262306a36Sopenharmony_ci			break;
57362306a36Sopenharmony_ci		case STATE_SYSEX_2:
57462306a36Sopenharmony_ci			p[0] |= 0x04;
57562306a36Sopenharmony_ci			p[1] = port->data[0];
57662306a36Sopenharmony_ci			p[2] = port->data[1];
57762306a36Sopenharmony_ci			p[3] = b;
57862306a36Sopenharmony_ci			next_state = STATE_SYSEX_0;
57962306a36Sopenharmony_ci			break;
58062306a36Sopenharmony_ci		}
58162306a36Sopenharmony_ci		break;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	/* States where we have to write into the USB request */
58562306a36Sopenharmony_ci	if (next_state == STATE_FINISHED ||
58662306a36Sopenharmony_ci	    port->state == STATE_SYSEX_2 ||
58762306a36Sopenharmony_ci	    port->state == STATE_1PARAM ||
58862306a36Sopenharmony_ci	    port->state == STATE_2PARAM_2 ||
58962306a36Sopenharmony_ci	    port->state == STATE_REAL_TIME) {
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		unsigned int length = req->length;
59262306a36Sopenharmony_ci		u8 *buf = (u8 *)req->buf + length;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci		memcpy(buf, p, sizeof(p));
59562306a36Sopenharmony_ci		req->length = length + sizeof(p);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		if (next_state == STATE_FINISHED) {
59862306a36Sopenharmony_ci			next_state = STATE_INITIAL;
59962306a36Sopenharmony_ci			port->data[0] = port->data[1] = 0;
60062306a36Sopenharmony_ci		}
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	port->state = next_state;
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic int f_midi_do_transmit(struct f_midi *midi, struct usb_ep *ep)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	struct usb_request *req = NULL;
60962306a36Sopenharmony_ci	unsigned int len, i;
61062306a36Sopenharmony_ci	bool active = false;
61162306a36Sopenharmony_ci	int err;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	/*
61462306a36Sopenharmony_ci	 * We peek the request in order to reuse it if it fails to enqueue on
61562306a36Sopenharmony_ci	 * its endpoint
61662306a36Sopenharmony_ci	 */
61762306a36Sopenharmony_ci	len = kfifo_peek(&midi->in_req_fifo, &req);
61862306a36Sopenharmony_ci	if (len != 1) {
61962306a36Sopenharmony_ci		ERROR(midi, "%s: Couldn't get usb request\n", __func__);
62062306a36Sopenharmony_ci		return -1;
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	/*
62462306a36Sopenharmony_ci	 * If buffer overrun, then we ignore this transmission.
62562306a36Sopenharmony_ci	 * IMPORTANT: This will cause the user-space rawmidi device to block
62662306a36Sopenharmony_ci	 * until a) usb requests have been completed or b) snd_rawmidi_write()
62762306a36Sopenharmony_ci	 * times out.
62862306a36Sopenharmony_ci	 */
62962306a36Sopenharmony_ci	if (req->length > 0)
63062306a36Sopenharmony_ci		return 0;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	for (i = midi->in_last_port; i < midi->in_ports; ++i) {
63362306a36Sopenharmony_ci		struct gmidi_in_port *port = midi->in_ports_array + i;
63462306a36Sopenharmony_ci		struct snd_rawmidi_substream *substream = port->substream;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci		if (!port->active || !substream)
63762306a36Sopenharmony_ci			continue;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci		while (req->length + 3 < midi->buflen) {
64062306a36Sopenharmony_ci			uint8_t b;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci			if (snd_rawmidi_transmit(substream, &b, 1) != 1) {
64362306a36Sopenharmony_ci				port->active = 0;
64462306a36Sopenharmony_ci				break;
64562306a36Sopenharmony_ci			}
64662306a36Sopenharmony_ci			f_midi_transmit_byte(req, port, b);
64762306a36Sopenharmony_ci		}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci		active = !!port->active;
65062306a36Sopenharmony_ci		if (active)
65162306a36Sopenharmony_ci			break;
65262306a36Sopenharmony_ci	}
65362306a36Sopenharmony_ci	midi->in_last_port = active ? i : 0;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	if (req->length <= 0)
65662306a36Sopenharmony_ci		goto done;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	err = usb_ep_queue(ep, req, GFP_ATOMIC);
65962306a36Sopenharmony_ci	if (err < 0) {
66062306a36Sopenharmony_ci		ERROR(midi, "%s failed to queue req: %d\n",
66162306a36Sopenharmony_ci		      midi->in_ep->name, err);
66262306a36Sopenharmony_ci		req->length = 0; /* Re-use request next time. */
66362306a36Sopenharmony_ci	} else {
66462306a36Sopenharmony_ci		/* Upon success, put request at the back of the queue. */
66562306a36Sopenharmony_ci		kfifo_skip(&midi->in_req_fifo);
66662306a36Sopenharmony_ci		kfifo_put(&midi->in_req_fifo, req);
66762306a36Sopenharmony_ci	}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cidone:
67062306a36Sopenharmony_ci	return active;
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistatic void f_midi_transmit(struct f_midi *midi)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	struct usb_ep *ep = midi->in_ep;
67662306a36Sopenharmony_ci	int ret;
67762306a36Sopenharmony_ci	unsigned long flags;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	/* We only care about USB requests if IN endpoint is enabled */
68062306a36Sopenharmony_ci	if (!ep || !ep->enabled)
68162306a36Sopenharmony_ci		goto drop_out;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	spin_lock_irqsave(&midi->transmit_lock, flags);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	do {
68662306a36Sopenharmony_ci		ret = f_midi_do_transmit(midi, ep);
68762306a36Sopenharmony_ci		if (ret < 0) {
68862306a36Sopenharmony_ci			spin_unlock_irqrestore(&midi->transmit_lock, flags);
68962306a36Sopenharmony_ci			goto drop_out;
69062306a36Sopenharmony_ci		}
69162306a36Sopenharmony_ci	} while (ret);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	spin_unlock_irqrestore(&midi->transmit_lock, flags);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	return;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_cidrop_out:
69862306a36Sopenharmony_ci	f_midi_drop_out_substreams(midi);
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic void f_midi_in_work(struct work_struct *work)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	struct f_midi *midi;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	midi = container_of(work, struct f_midi, work);
70662306a36Sopenharmony_ci	f_midi_transmit(midi);
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_cistatic int f_midi_in_open(struct snd_rawmidi_substream *substream)
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	struct f_midi *midi = substream->rmidi->private_data;
71262306a36Sopenharmony_ci	struct gmidi_in_port *port;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	if (substream->number >= midi->in_ports)
71562306a36Sopenharmony_ci		return -EINVAL;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	VDBG(midi, "%s()\n", __func__);
71862306a36Sopenharmony_ci	port = midi->in_ports_array + substream->number;
71962306a36Sopenharmony_ci	port->substream = substream;
72062306a36Sopenharmony_ci	port->state = STATE_INITIAL;
72162306a36Sopenharmony_ci	return 0;
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cistatic int f_midi_in_close(struct snd_rawmidi_substream *substream)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	struct f_midi *midi = substream->rmidi->private_data;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	VDBG(midi, "%s()\n", __func__);
72962306a36Sopenharmony_ci	return 0;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic void f_midi_in_trigger(struct snd_rawmidi_substream *substream, int up)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	struct f_midi *midi = substream->rmidi->private_data;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (substream->number >= midi->in_ports)
73762306a36Sopenharmony_ci		return;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	VDBG(midi, "%s() %d\n", __func__, up);
74062306a36Sopenharmony_ci	midi->in_ports_array[substream->number].active = up;
74162306a36Sopenharmony_ci	if (up)
74262306a36Sopenharmony_ci		queue_work(system_highpri_wq, &midi->work);
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_cistatic int f_midi_out_open(struct snd_rawmidi_substream *substream)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	struct f_midi *midi = substream->rmidi->private_data;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	if (substream->number >= MAX_PORTS)
75062306a36Sopenharmony_ci		return -EINVAL;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	VDBG(midi, "%s()\n", __func__);
75362306a36Sopenharmony_ci	midi->out_substream[substream->number] = substream;
75462306a36Sopenharmony_ci	return 0;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic int f_midi_out_close(struct snd_rawmidi_substream *substream)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	struct f_midi *midi = substream->rmidi->private_data;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	VDBG(midi, "%s()\n", __func__);
76262306a36Sopenharmony_ci	return 0;
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic void f_midi_out_trigger(struct snd_rawmidi_substream *substream, int up)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	struct f_midi *midi = substream->rmidi->private_data;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	VDBG(midi, "%s()\n", __func__);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	if (up)
77262306a36Sopenharmony_ci		set_bit(substream->number, &midi->out_triggered);
77362306a36Sopenharmony_ci	else
77462306a36Sopenharmony_ci		clear_bit(substream->number, &midi->out_triggered);
77562306a36Sopenharmony_ci}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_cistatic const struct snd_rawmidi_ops gmidi_in_ops = {
77862306a36Sopenharmony_ci	.open = f_midi_in_open,
77962306a36Sopenharmony_ci	.close = f_midi_in_close,
78062306a36Sopenharmony_ci	.trigger = f_midi_in_trigger,
78162306a36Sopenharmony_ci};
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_cistatic const struct snd_rawmidi_ops gmidi_out_ops = {
78462306a36Sopenharmony_ci	.open = f_midi_out_open,
78562306a36Sopenharmony_ci	.close = f_midi_out_close,
78662306a36Sopenharmony_ci	.trigger = f_midi_out_trigger
78762306a36Sopenharmony_ci};
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_cistatic inline void f_midi_unregister_card(struct f_midi *midi)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	if (midi->card) {
79262306a36Sopenharmony_ci		snd_card_free(midi->card);
79362306a36Sopenharmony_ci		midi->card = NULL;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci/* register as a sound "card" */
79862306a36Sopenharmony_cistatic int f_midi_register_card(struct f_midi *midi)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	struct snd_card *card;
80162306a36Sopenharmony_ci	struct snd_rawmidi *rmidi;
80262306a36Sopenharmony_ci	int err;
80362306a36Sopenharmony_ci	static struct snd_device_ops ops = {
80462306a36Sopenharmony_ci		.dev_free = f_midi_snd_free,
80562306a36Sopenharmony_ci	};
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	err = snd_card_new(&midi->gadget->dev, midi->index, midi->id,
80862306a36Sopenharmony_ci			   THIS_MODULE, 0, &card);
80962306a36Sopenharmony_ci	if (err < 0) {
81062306a36Sopenharmony_ci		ERROR(midi, "snd_card_new() failed\n");
81162306a36Sopenharmony_ci		goto fail;
81262306a36Sopenharmony_ci	}
81362306a36Sopenharmony_ci	midi->card = card;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, midi, &ops);
81662306a36Sopenharmony_ci	if (err < 0) {
81762306a36Sopenharmony_ci		ERROR(midi, "snd_device_new() failed: error %d\n", err);
81862306a36Sopenharmony_ci		goto fail;
81962306a36Sopenharmony_ci	}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	strcpy(card->driver, f_midi_longname);
82262306a36Sopenharmony_ci	strcpy(card->longname, f_midi_longname);
82362306a36Sopenharmony_ci	strcpy(card->shortname, f_midi_shortname);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	/* Set up rawmidi */
82662306a36Sopenharmony_ci	snd_component_add(card, "MIDI");
82762306a36Sopenharmony_ci	err = snd_rawmidi_new(card, card->longname, 0,
82862306a36Sopenharmony_ci			      midi->out_ports, midi->in_ports, &rmidi);
82962306a36Sopenharmony_ci	if (err < 0) {
83062306a36Sopenharmony_ci		ERROR(midi, "snd_rawmidi_new() failed: error %d\n", err);
83162306a36Sopenharmony_ci		goto fail;
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci	midi->rmidi = rmidi;
83462306a36Sopenharmony_ci	midi->in_last_port = 0;
83562306a36Sopenharmony_ci	strcpy(rmidi->name, card->shortname);
83662306a36Sopenharmony_ci	rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
83762306a36Sopenharmony_ci			    SNDRV_RAWMIDI_INFO_INPUT |
83862306a36Sopenharmony_ci			    SNDRV_RAWMIDI_INFO_DUPLEX;
83962306a36Sopenharmony_ci	rmidi->private_data = midi;
84062306a36Sopenharmony_ci	rmidi->private_free = f_midi_rmidi_free;
84162306a36Sopenharmony_ci	midi->free_ref++;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	/*
84462306a36Sopenharmony_ci	 * Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT.
84562306a36Sopenharmony_ci	 * It's an upside-down world being a gadget.
84662306a36Sopenharmony_ci	 */
84762306a36Sopenharmony_ci	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops);
84862306a36Sopenharmony_ci	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	/* register it - we're ready to go */
85162306a36Sopenharmony_ci	err = snd_card_register(card);
85262306a36Sopenharmony_ci	if (err < 0) {
85362306a36Sopenharmony_ci		ERROR(midi, "snd_card_register() failed\n");
85462306a36Sopenharmony_ci		goto fail;
85562306a36Sopenharmony_ci	}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	VDBG(midi, "%s() finished ok\n", __func__);
85862306a36Sopenharmony_ci	return 0;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_cifail:
86162306a36Sopenharmony_ci	f_midi_unregister_card(midi);
86262306a36Sopenharmony_ci	return err;
86362306a36Sopenharmony_ci}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci/* MIDI function driver setup/binding */
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_cistatic int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
86862306a36Sopenharmony_ci{
86962306a36Sopenharmony_ci	struct usb_descriptor_header **midi_function;
87062306a36Sopenharmony_ci	struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS];
87162306a36Sopenharmony_ci	struct usb_midi_in_jack_descriptor jack_in_emb_desc[MAX_PORTS];
87262306a36Sopenharmony_ci	struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc[MAX_PORTS];
87362306a36Sopenharmony_ci	struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc[MAX_PORTS];
87462306a36Sopenharmony_ci	struct usb_composite_dev *cdev = c->cdev;
87562306a36Sopenharmony_ci	struct f_midi *midi = func_to_midi(f);
87662306a36Sopenharmony_ci	struct usb_string *us;
87762306a36Sopenharmony_ci	int status, n, jack = 1, i = 0, endpoint_descriptor_index = 0;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	midi->gadget = cdev->gadget;
88062306a36Sopenharmony_ci	INIT_WORK(&midi->work, f_midi_in_work);
88162306a36Sopenharmony_ci	status = f_midi_register_card(midi);
88262306a36Sopenharmony_ci	if (status < 0)
88362306a36Sopenharmony_ci		goto fail_register;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	/* maybe allocate device-global string ID */
88662306a36Sopenharmony_ci	us = usb_gstrings_attach(c->cdev, midi_strings,
88762306a36Sopenharmony_ci				 ARRAY_SIZE(midi_string_defs));
88862306a36Sopenharmony_ci	if (IS_ERR(us)) {
88962306a36Sopenharmony_ci		status = PTR_ERR(us);
89062306a36Sopenharmony_ci		goto fail;
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci	ac_interface_desc.iInterface = us[STRING_FUNC_IDX].id;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	/* We have two interfaces, AudioControl and MIDIStreaming */
89562306a36Sopenharmony_ci	status = usb_interface_id(c, f);
89662306a36Sopenharmony_ci	if (status < 0)
89762306a36Sopenharmony_ci		goto fail;
89862306a36Sopenharmony_ci	ac_interface_desc.bInterfaceNumber = status;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	status = usb_interface_id(c, f);
90162306a36Sopenharmony_ci	if (status < 0)
90262306a36Sopenharmony_ci		goto fail;
90362306a36Sopenharmony_ci	ms_interface_desc.bInterfaceNumber = status;
90462306a36Sopenharmony_ci	ac_header_desc.baInterfaceNr[0] = status;
90562306a36Sopenharmony_ci	midi->ms_id = status;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	status = -ENODEV;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	/* allocate instance-specific endpoints */
91062306a36Sopenharmony_ci	midi->in_ep = usb_ep_autoconfig(cdev->gadget, &bulk_in_desc);
91162306a36Sopenharmony_ci	if (!midi->in_ep)
91262306a36Sopenharmony_ci		goto fail;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	midi->out_ep = usb_ep_autoconfig(cdev->gadget, &bulk_out_desc);
91562306a36Sopenharmony_ci	if (!midi->out_ep)
91662306a36Sopenharmony_ci		goto fail;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	/* allocate temporary function list */
91962306a36Sopenharmony_ci	midi_function = kcalloc((MAX_PORTS * 4) + 11, sizeof(*midi_function),
92062306a36Sopenharmony_ci				GFP_KERNEL);
92162306a36Sopenharmony_ci	if (!midi_function) {
92262306a36Sopenharmony_ci		status = -ENOMEM;
92362306a36Sopenharmony_ci		goto fail;
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	/*
92762306a36Sopenharmony_ci	 * construct the function's descriptor set. As the number of
92862306a36Sopenharmony_ci	 * input and output MIDI ports is configurable, we have to do
92962306a36Sopenharmony_ci	 * it that way.
93062306a36Sopenharmony_ci	 */
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	/* add the headers - these are always the same */
93362306a36Sopenharmony_ci	midi_function[i++] = (struct usb_descriptor_header *) &ac_interface_desc;
93462306a36Sopenharmony_ci	midi_function[i++] = (struct usb_descriptor_header *) &ac_header_desc;
93562306a36Sopenharmony_ci	midi_function[i++] = (struct usb_descriptor_header *) &ms_interface_desc;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	/* calculate the header's wTotalLength */
93862306a36Sopenharmony_ci	n = USB_DT_MS_HEADER_SIZE
93962306a36Sopenharmony_ci		+ (midi->in_ports + midi->out_ports) *
94062306a36Sopenharmony_ci			(USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1));
94162306a36Sopenharmony_ci	ms_header_desc.wTotalLength = cpu_to_le16(n);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	midi_function[i++] = (struct usb_descriptor_header *) &ms_header_desc;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	/* configure the external IN jacks, each linked to an embedded OUT jack */
94662306a36Sopenharmony_ci	for (n = 0; n < midi->in_ports; n++) {
94762306a36Sopenharmony_ci		struct usb_midi_in_jack_descriptor *in_ext = &jack_in_ext_desc[n];
94862306a36Sopenharmony_ci		struct usb_midi_out_jack_descriptor_1 *out_emb = &jack_out_emb_desc[n];
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci		in_ext->bLength			= USB_DT_MIDI_IN_SIZE;
95162306a36Sopenharmony_ci		in_ext->bDescriptorType		= USB_DT_CS_INTERFACE;
95262306a36Sopenharmony_ci		in_ext->bDescriptorSubtype	= USB_MS_MIDI_IN_JACK;
95362306a36Sopenharmony_ci		in_ext->bJackType		= USB_MS_EXTERNAL;
95462306a36Sopenharmony_ci		in_ext->bJackID			= jack++;
95562306a36Sopenharmony_ci		in_ext->iJack			= 0;
95662306a36Sopenharmony_ci		midi_function[i++] = (struct usb_descriptor_header *) in_ext;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci		out_emb->bLength		= USB_DT_MIDI_OUT_SIZE(1);
95962306a36Sopenharmony_ci		out_emb->bDescriptorType	= USB_DT_CS_INTERFACE;
96062306a36Sopenharmony_ci		out_emb->bDescriptorSubtype	= USB_MS_MIDI_OUT_JACK;
96162306a36Sopenharmony_ci		out_emb->bJackType		= USB_MS_EMBEDDED;
96262306a36Sopenharmony_ci		out_emb->bJackID		= jack++;
96362306a36Sopenharmony_ci		out_emb->bNrInputPins		= 1;
96462306a36Sopenharmony_ci		out_emb->pins[0].baSourcePin	= 1;
96562306a36Sopenharmony_ci		out_emb->pins[0].baSourceID	= in_ext->bJackID;
96662306a36Sopenharmony_ci		out_emb->iJack			= 0;
96762306a36Sopenharmony_ci		midi_function[i++] = (struct usb_descriptor_header *) out_emb;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci		/* link it to the endpoint */
97062306a36Sopenharmony_ci		ms_in_desc.baAssocJackID[n] = out_emb->bJackID;
97162306a36Sopenharmony_ci	}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	/* configure the external OUT jacks, each linked to an embedded IN jack */
97462306a36Sopenharmony_ci	for (n = 0; n < midi->out_ports; n++) {
97562306a36Sopenharmony_ci		struct usb_midi_in_jack_descriptor *in_emb = &jack_in_emb_desc[n];
97662306a36Sopenharmony_ci		struct usb_midi_out_jack_descriptor_1 *out_ext = &jack_out_ext_desc[n];
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci		in_emb->bLength			= USB_DT_MIDI_IN_SIZE;
97962306a36Sopenharmony_ci		in_emb->bDescriptorType		= USB_DT_CS_INTERFACE;
98062306a36Sopenharmony_ci		in_emb->bDescriptorSubtype	= USB_MS_MIDI_IN_JACK;
98162306a36Sopenharmony_ci		in_emb->bJackType		= USB_MS_EMBEDDED;
98262306a36Sopenharmony_ci		in_emb->bJackID			= jack++;
98362306a36Sopenharmony_ci		in_emb->iJack			= 0;
98462306a36Sopenharmony_ci		midi_function[i++] = (struct usb_descriptor_header *) in_emb;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci		out_ext->bLength =		USB_DT_MIDI_OUT_SIZE(1);
98762306a36Sopenharmony_ci		out_ext->bDescriptorType =	USB_DT_CS_INTERFACE;
98862306a36Sopenharmony_ci		out_ext->bDescriptorSubtype =	USB_MS_MIDI_OUT_JACK;
98962306a36Sopenharmony_ci		out_ext->bJackType =		USB_MS_EXTERNAL;
99062306a36Sopenharmony_ci		out_ext->bJackID =		jack++;
99162306a36Sopenharmony_ci		out_ext->bNrInputPins =		1;
99262306a36Sopenharmony_ci		out_ext->iJack =		0;
99362306a36Sopenharmony_ci		out_ext->pins[0].baSourceID =	in_emb->bJackID;
99462306a36Sopenharmony_ci		out_ext->pins[0].baSourcePin =	1;
99562306a36Sopenharmony_ci		midi_function[i++] = (struct usb_descriptor_header *) out_ext;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci		/* link it to the endpoint */
99862306a36Sopenharmony_ci		ms_out_desc.baAssocJackID[n] = in_emb->bJackID;
99962306a36Sopenharmony_ci	}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	/* configure the endpoint descriptors ... */
100262306a36Sopenharmony_ci	ms_out_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->in_ports);
100362306a36Sopenharmony_ci	ms_out_desc.bNumEmbMIDIJack = midi->in_ports;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	ms_in_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->out_ports);
100662306a36Sopenharmony_ci	ms_in_desc.bNumEmbMIDIJack = midi->out_ports;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	/* ... and add them to the list */
100962306a36Sopenharmony_ci	endpoint_descriptor_index = i;
101062306a36Sopenharmony_ci	midi_function[i++] = (struct usb_descriptor_header *) &bulk_out_desc;
101162306a36Sopenharmony_ci	midi_function[i++] = (struct usb_descriptor_header *) &ms_out_desc;
101262306a36Sopenharmony_ci	midi_function[i++] = (struct usb_descriptor_header *) &bulk_in_desc;
101362306a36Sopenharmony_ci	midi_function[i++] = (struct usb_descriptor_header *) &ms_in_desc;
101462306a36Sopenharmony_ci	midi_function[i++] = NULL;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	/*
101762306a36Sopenharmony_ci	 * support all relevant hardware speeds... we expect that when
101862306a36Sopenharmony_ci	 * hardware is dual speed, all bulk-capable endpoints work at
101962306a36Sopenharmony_ci	 * both speeds
102062306a36Sopenharmony_ci	 */
102162306a36Sopenharmony_ci	/* copy descriptors, and track endpoint copies */
102262306a36Sopenharmony_ci	f->fs_descriptors = usb_copy_descriptors(midi_function);
102362306a36Sopenharmony_ci	if (!f->fs_descriptors)
102462306a36Sopenharmony_ci		goto fail_f_midi;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	bulk_in_desc.wMaxPacketSize = cpu_to_le16(512);
102762306a36Sopenharmony_ci	bulk_out_desc.wMaxPacketSize = cpu_to_le16(512);
102862306a36Sopenharmony_ci	f->hs_descriptors = usb_copy_descriptors(midi_function);
102962306a36Sopenharmony_ci	if (!f->hs_descriptors)
103062306a36Sopenharmony_ci		goto fail_f_midi;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	bulk_in_desc.wMaxPacketSize = cpu_to_le16(1024);
103362306a36Sopenharmony_ci	bulk_out_desc.wMaxPacketSize = cpu_to_le16(1024);
103462306a36Sopenharmony_ci	i = endpoint_descriptor_index;
103562306a36Sopenharmony_ci	midi_function[i++] = (struct usb_descriptor_header *)
103662306a36Sopenharmony_ci			     &bulk_out_desc;
103762306a36Sopenharmony_ci	midi_function[i++] = (struct usb_descriptor_header *)
103862306a36Sopenharmony_ci			     &bulk_out_ss_comp_desc;
103962306a36Sopenharmony_ci	midi_function[i++] = (struct usb_descriptor_header *)
104062306a36Sopenharmony_ci			     &ms_out_desc;
104162306a36Sopenharmony_ci	midi_function[i++] = (struct usb_descriptor_header *)
104262306a36Sopenharmony_ci			     &bulk_in_desc;
104362306a36Sopenharmony_ci	midi_function[i++] = (struct usb_descriptor_header *)
104462306a36Sopenharmony_ci			     &bulk_in_ss_comp_desc;
104562306a36Sopenharmony_ci	midi_function[i++] = (struct usb_descriptor_header *)
104662306a36Sopenharmony_ci			     &ms_in_desc;
104762306a36Sopenharmony_ci	f->ss_descriptors = usb_copy_descriptors(midi_function);
104862306a36Sopenharmony_ci	if (!f->ss_descriptors)
104962306a36Sopenharmony_ci		goto fail_f_midi;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	kfree(midi_function);
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	return 0;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_cifail_f_midi:
105662306a36Sopenharmony_ci	kfree(midi_function);
105762306a36Sopenharmony_ci	usb_free_all_descriptors(f);
105862306a36Sopenharmony_cifail:
105962306a36Sopenharmony_ci	f_midi_unregister_card(midi);
106062306a36Sopenharmony_cifail_register:
106162306a36Sopenharmony_ci	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	return status;
106462306a36Sopenharmony_ci}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_cistatic inline struct f_midi_opts *to_f_midi_opts(struct config_item *item)
106762306a36Sopenharmony_ci{
106862306a36Sopenharmony_ci	return container_of(to_config_group(item), struct f_midi_opts,
106962306a36Sopenharmony_ci			    func_inst.group);
107062306a36Sopenharmony_ci}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_cistatic void midi_attr_release(struct config_item *item)
107362306a36Sopenharmony_ci{
107462306a36Sopenharmony_ci	struct f_midi_opts *opts = to_f_midi_opts(item);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	usb_put_function_instance(&opts->func_inst);
107762306a36Sopenharmony_ci}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_cistatic struct configfs_item_operations midi_item_ops = {
108062306a36Sopenharmony_ci	.release	= midi_attr_release,
108162306a36Sopenharmony_ci};
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci#define F_MIDI_OPT(name, test_limit, limit)				\
108462306a36Sopenharmony_cistatic ssize_t f_midi_opts_##name##_show(struct config_item *item, char *page) \
108562306a36Sopenharmony_ci{									\
108662306a36Sopenharmony_ci	struct f_midi_opts *opts = to_f_midi_opts(item);		\
108762306a36Sopenharmony_ci	int result;							\
108862306a36Sopenharmony_ci									\
108962306a36Sopenharmony_ci	mutex_lock(&opts->lock);					\
109062306a36Sopenharmony_ci	result = sprintf(page, "%u\n", opts->name);			\
109162306a36Sopenharmony_ci	mutex_unlock(&opts->lock);					\
109262306a36Sopenharmony_ci									\
109362306a36Sopenharmony_ci	return result;							\
109462306a36Sopenharmony_ci}									\
109562306a36Sopenharmony_ci									\
109662306a36Sopenharmony_cistatic ssize_t f_midi_opts_##name##_store(struct config_item *item,	\
109762306a36Sopenharmony_ci					 const char *page, size_t len)	\
109862306a36Sopenharmony_ci{									\
109962306a36Sopenharmony_ci	struct f_midi_opts *opts = to_f_midi_opts(item);		\
110062306a36Sopenharmony_ci	int ret;							\
110162306a36Sopenharmony_ci	u32 num;							\
110262306a36Sopenharmony_ci									\
110362306a36Sopenharmony_ci	mutex_lock(&opts->lock);					\
110462306a36Sopenharmony_ci	if (opts->refcnt > 1) {						\
110562306a36Sopenharmony_ci		ret = -EBUSY;						\
110662306a36Sopenharmony_ci		goto end;						\
110762306a36Sopenharmony_ci	}								\
110862306a36Sopenharmony_ci									\
110962306a36Sopenharmony_ci	ret = kstrtou32(page, 0, &num);					\
111062306a36Sopenharmony_ci	if (ret)							\
111162306a36Sopenharmony_ci		goto end;						\
111262306a36Sopenharmony_ci									\
111362306a36Sopenharmony_ci	if (test_limit && num > limit) {				\
111462306a36Sopenharmony_ci		ret = -EINVAL;						\
111562306a36Sopenharmony_ci		goto end;						\
111662306a36Sopenharmony_ci	}								\
111762306a36Sopenharmony_ci	opts->name = num;						\
111862306a36Sopenharmony_ci	ret = len;							\
111962306a36Sopenharmony_ci									\
112062306a36Sopenharmony_ciend:									\
112162306a36Sopenharmony_ci	mutex_unlock(&opts->lock);					\
112262306a36Sopenharmony_ci	return ret;							\
112362306a36Sopenharmony_ci}									\
112462306a36Sopenharmony_ci									\
112562306a36Sopenharmony_ciCONFIGFS_ATTR(f_midi_opts_, name);
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci#define F_MIDI_OPT_SIGNED(name, test_limit, limit)				\
112862306a36Sopenharmony_cistatic ssize_t f_midi_opts_##name##_show(struct config_item *item, char *page) \
112962306a36Sopenharmony_ci{									\
113062306a36Sopenharmony_ci	struct f_midi_opts *opts = to_f_midi_opts(item);		\
113162306a36Sopenharmony_ci	int result;							\
113262306a36Sopenharmony_ci									\
113362306a36Sopenharmony_ci	mutex_lock(&opts->lock);					\
113462306a36Sopenharmony_ci	result = sprintf(page, "%d\n", opts->name);			\
113562306a36Sopenharmony_ci	mutex_unlock(&opts->lock);					\
113662306a36Sopenharmony_ci									\
113762306a36Sopenharmony_ci	return result;							\
113862306a36Sopenharmony_ci}									\
113962306a36Sopenharmony_ci									\
114062306a36Sopenharmony_cistatic ssize_t f_midi_opts_##name##_store(struct config_item *item,	\
114162306a36Sopenharmony_ci					 const char *page, size_t len)	\
114262306a36Sopenharmony_ci{									\
114362306a36Sopenharmony_ci	struct f_midi_opts *opts = to_f_midi_opts(item);		\
114462306a36Sopenharmony_ci	int ret;							\
114562306a36Sopenharmony_ci	s32 num;							\
114662306a36Sopenharmony_ci									\
114762306a36Sopenharmony_ci	mutex_lock(&opts->lock);					\
114862306a36Sopenharmony_ci	if (opts->refcnt > 1) {						\
114962306a36Sopenharmony_ci		ret = -EBUSY;						\
115062306a36Sopenharmony_ci		goto end;						\
115162306a36Sopenharmony_ci	}								\
115262306a36Sopenharmony_ci									\
115362306a36Sopenharmony_ci	ret = kstrtos32(page, 0, &num);					\
115462306a36Sopenharmony_ci	if (ret)							\
115562306a36Sopenharmony_ci		goto end;						\
115662306a36Sopenharmony_ci									\
115762306a36Sopenharmony_ci	if (test_limit && num > limit) {				\
115862306a36Sopenharmony_ci		ret = -EINVAL;						\
115962306a36Sopenharmony_ci		goto end;						\
116062306a36Sopenharmony_ci	}								\
116162306a36Sopenharmony_ci	opts->name = num;						\
116262306a36Sopenharmony_ci	ret = len;							\
116362306a36Sopenharmony_ci									\
116462306a36Sopenharmony_ciend:									\
116562306a36Sopenharmony_ci	mutex_unlock(&opts->lock);					\
116662306a36Sopenharmony_ci	return ret;							\
116762306a36Sopenharmony_ci}									\
116862306a36Sopenharmony_ci									\
116962306a36Sopenharmony_ciCONFIGFS_ATTR(f_midi_opts_, name);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ciF_MIDI_OPT_SIGNED(index, true, SNDRV_CARDS);
117262306a36Sopenharmony_ciF_MIDI_OPT(buflen, false, 0);
117362306a36Sopenharmony_ciF_MIDI_OPT(qlen, false, 0);
117462306a36Sopenharmony_ciF_MIDI_OPT(in_ports, true, MAX_PORTS);
117562306a36Sopenharmony_ciF_MIDI_OPT(out_ports, true, MAX_PORTS);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_cistatic ssize_t f_midi_opts_id_show(struct config_item *item, char *page)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	struct f_midi_opts *opts = to_f_midi_opts(item);
118062306a36Sopenharmony_ci	int result;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	mutex_lock(&opts->lock);
118362306a36Sopenharmony_ci	if (opts->id) {
118462306a36Sopenharmony_ci		result = strlcpy(page, opts->id, PAGE_SIZE);
118562306a36Sopenharmony_ci	} else {
118662306a36Sopenharmony_ci		page[0] = 0;
118762306a36Sopenharmony_ci		result = 0;
118862306a36Sopenharmony_ci	}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	return result;
119362306a36Sopenharmony_ci}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_cistatic ssize_t f_midi_opts_id_store(struct config_item *item,
119662306a36Sopenharmony_ci				    const char *page, size_t len)
119762306a36Sopenharmony_ci{
119862306a36Sopenharmony_ci	struct f_midi_opts *opts = to_f_midi_opts(item);
119962306a36Sopenharmony_ci	int ret;
120062306a36Sopenharmony_ci	char *c;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	mutex_lock(&opts->lock);
120362306a36Sopenharmony_ci	if (opts->refcnt > 1) {
120462306a36Sopenharmony_ci		ret = -EBUSY;
120562306a36Sopenharmony_ci		goto end;
120662306a36Sopenharmony_ci	}
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	c = kstrndup(page, len, GFP_KERNEL);
120962306a36Sopenharmony_ci	if (!c) {
121062306a36Sopenharmony_ci		ret = -ENOMEM;
121162306a36Sopenharmony_ci		goto end;
121262306a36Sopenharmony_ci	}
121362306a36Sopenharmony_ci	if (opts->id_allocated)
121462306a36Sopenharmony_ci		kfree(opts->id);
121562306a36Sopenharmony_ci	opts->id = c;
121662306a36Sopenharmony_ci	opts->id_allocated = true;
121762306a36Sopenharmony_ci	ret = len;
121862306a36Sopenharmony_ciend:
121962306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
122062306a36Sopenharmony_ci	return ret;
122162306a36Sopenharmony_ci}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ciCONFIGFS_ATTR(f_midi_opts_, id);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_cistatic struct configfs_attribute *midi_attrs[] = {
122662306a36Sopenharmony_ci	&f_midi_opts_attr_index,
122762306a36Sopenharmony_ci	&f_midi_opts_attr_buflen,
122862306a36Sopenharmony_ci	&f_midi_opts_attr_qlen,
122962306a36Sopenharmony_ci	&f_midi_opts_attr_in_ports,
123062306a36Sopenharmony_ci	&f_midi_opts_attr_out_ports,
123162306a36Sopenharmony_ci	&f_midi_opts_attr_id,
123262306a36Sopenharmony_ci	NULL,
123362306a36Sopenharmony_ci};
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_cistatic const struct config_item_type midi_func_type = {
123662306a36Sopenharmony_ci	.ct_item_ops	= &midi_item_ops,
123762306a36Sopenharmony_ci	.ct_attrs	= midi_attrs,
123862306a36Sopenharmony_ci	.ct_owner	= THIS_MODULE,
123962306a36Sopenharmony_ci};
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_cistatic void f_midi_free_inst(struct usb_function_instance *f)
124262306a36Sopenharmony_ci{
124362306a36Sopenharmony_ci	struct f_midi_opts *opts;
124462306a36Sopenharmony_ci	bool free = false;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	opts = container_of(f, struct f_midi_opts, func_inst);
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	mutex_lock(&opts->lock);
124962306a36Sopenharmony_ci	if (!--opts->refcnt) {
125062306a36Sopenharmony_ci		free = true;
125162306a36Sopenharmony_ci	}
125262306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	if (free) {
125562306a36Sopenharmony_ci		if (opts->id_allocated)
125662306a36Sopenharmony_ci			kfree(opts->id);
125762306a36Sopenharmony_ci		kfree(opts);
125862306a36Sopenharmony_ci	}
125962306a36Sopenharmony_ci}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_cistatic struct usb_function_instance *f_midi_alloc_inst(void)
126262306a36Sopenharmony_ci{
126362306a36Sopenharmony_ci	struct f_midi_opts *opts;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
126662306a36Sopenharmony_ci	if (!opts)
126762306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	mutex_init(&opts->lock);
127062306a36Sopenharmony_ci	opts->func_inst.free_func_inst = f_midi_free_inst;
127162306a36Sopenharmony_ci	opts->index = SNDRV_DEFAULT_IDX1;
127262306a36Sopenharmony_ci	opts->id = SNDRV_DEFAULT_STR1;
127362306a36Sopenharmony_ci	opts->buflen = 512;
127462306a36Sopenharmony_ci	opts->qlen = 32;
127562306a36Sopenharmony_ci	opts->in_ports = 1;
127662306a36Sopenharmony_ci	opts->out_ports = 1;
127762306a36Sopenharmony_ci	opts->refcnt = 1;
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	config_group_init_type_name(&opts->func_inst.group, "",
128062306a36Sopenharmony_ci				    &midi_func_type);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	return &opts->func_inst;
128362306a36Sopenharmony_ci}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_cistatic void f_midi_free(struct usb_function *f)
128662306a36Sopenharmony_ci{
128762306a36Sopenharmony_ci	struct f_midi *midi;
128862306a36Sopenharmony_ci	struct f_midi_opts *opts;
128962306a36Sopenharmony_ci	bool free = false;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	midi = func_to_midi(f);
129262306a36Sopenharmony_ci	opts = container_of(f->fi, struct f_midi_opts, func_inst);
129362306a36Sopenharmony_ci	mutex_lock(&opts->lock);
129462306a36Sopenharmony_ci	if (!--midi->free_ref) {
129562306a36Sopenharmony_ci		kfree(midi->id);
129662306a36Sopenharmony_ci		kfifo_free(&midi->in_req_fifo);
129762306a36Sopenharmony_ci		kfree(midi);
129862306a36Sopenharmony_ci		free = true;
129962306a36Sopenharmony_ci	}
130062306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	if (free)
130362306a36Sopenharmony_ci		f_midi_free_inst(&opts->func_inst);
130462306a36Sopenharmony_ci}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_cistatic void f_midi_rmidi_free(struct snd_rawmidi *rmidi)
130762306a36Sopenharmony_ci{
130862306a36Sopenharmony_ci	f_midi_free(rmidi->private_data);
130962306a36Sopenharmony_ci}
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_cistatic void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
131262306a36Sopenharmony_ci{
131362306a36Sopenharmony_ci	struct usb_composite_dev *cdev = f->config->cdev;
131462306a36Sopenharmony_ci	struct f_midi *midi = func_to_midi(f);
131562306a36Sopenharmony_ci	struct snd_card *card;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	DBG(cdev, "unbind\n");
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	/* just to be sure */
132062306a36Sopenharmony_ci	f_midi_disable(f);
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	card = midi->card;
132362306a36Sopenharmony_ci	midi->card = NULL;
132462306a36Sopenharmony_ci	if (card)
132562306a36Sopenharmony_ci		snd_card_free_when_closed(card);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	usb_free_all_descriptors(f);
132862306a36Sopenharmony_ci}
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_cistatic struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
133162306a36Sopenharmony_ci{
133262306a36Sopenharmony_ci	struct f_midi *midi = NULL;
133362306a36Sopenharmony_ci	struct f_midi_opts *opts;
133462306a36Sopenharmony_ci	int status, i;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	opts = container_of(fi, struct f_midi_opts, func_inst);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	mutex_lock(&opts->lock);
133962306a36Sopenharmony_ci	/* sanity check */
134062306a36Sopenharmony_ci	if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS) {
134162306a36Sopenharmony_ci		status = -EINVAL;
134262306a36Sopenharmony_ci		goto setup_fail;
134362306a36Sopenharmony_ci	}
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	/* allocate and initialize one new instance */
134662306a36Sopenharmony_ci	midi = kzalloc(struct_size(midi, in_ports_array, opts->in_ports),
134762306a36Sopenharmony_ci		       GFP_KERNEL);
134862306a36Sopenharmony_ci	if (!midi) {
134962306a36Sopenharmony_ci		status = -ENOMEM;
135062306a36Sopenharmony_ci		goto setup_fail;
135162306a36Sopenharmony_ci	}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	for (i = 0; i < opts->in_ports; i++)
135462306a36Sopenharmony_ci		midi->in_ports_array[i].cable = i;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	/* set up ALSA midi devices */
135762306a36Sopenharmony_ci	midi->id = kstrdup(opts->id, GFP_KERNEL);
135862306a36Sopenharmony_ci	if (opts->id && !midi->id) {
135962306a36Sopenharmony_ci		status = -ENOMEM;
136062306a36Sopenharmony_ci		goto midi_free;
136162306a36Sopenharmony_ci	}
136262306a36Sopenharmony_ci	midi->in_ports = opts->in_ports;
136362306a36Sopenharmony_ci	midi->out_ports = opts->out_ports;
136462306a36Sopenharmony_ci	midi->index = opts->index;
136562306a36Sopenharmony_ci	midi->buflen = opts->buflen;
136662306a36Sopenharmony_ci	midi->qlen = opts->qlen;
136762306a36Sopenharmony_ci	midi->in_last_port = 0;
136862306a36Sopenharmony_ci	midi->free_ref = 1;
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	status = kfifo_alloc(&midi->in_req_fifo, midi->qlen, GFP_KERNEL);
137162306a36Sopenharmony_ci	if (status)
137262306a36Sopenharmony_ci		goto midi_free;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	spin_lock_init(&midi->transmit_lock);
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	++opts->refcnt;
137762306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	midi->func.name		= "gmidi function";
138062306a36Sopenharmony_ci	midi->func.bind		= f_midi_bind;
138162306a36Sopenharmony_ci	midi->func.unbind	= f_midi_unbind;
138262306a36Sopenharmony_ci	midi->func.set_alt	= f_midi_set_alt;
138362306a36Sopenharmony_ci	midi->func.disable	= f_midi_disable;
138462306a36Sopenharmony_ci	midi->func.free_func	= f_midi_free;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	return &midi->func;
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_cimidi_free:
138962306a36Sopenharmony_ci	if (midi)
139062306a36Sopenharmony_ci		kfree(midi->id);
139162306a36Sopenharmony_ci	kfree(midi);
139262306a36Sopenharmony_cisetup_fail:
139362306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	return ERR_PTR(status);
139662306a36Sopenharmony_ci}
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ciDECLARE_USB_FUNCTION_INIT(midi, f_midi_alloc_inst, f_midi_alloc);
1399