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