// SPDX-License-Identifier: GPL-2.0+/* * f_midi.c -- USB MIDI class function driver * * Copyright (C) 2006 Thumtronics Pty Ltd. * Developed for Thumtronics by Grey Innovation * Ben Williamson <ben.williamson@greyinnovation.com> * * Rewritten for the composite framework * Copyright (C) 2011 Daniel Mack <zonque@gmail.com> * * Based on drivers/usb/gadget/f_audio.c, * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> * Copyright (C) 2008 Analog Devices, Inc * * and drivers/usb/gadget/midi.c, * Copyright (C) 2006 Thumtronics Pty Ltd. * Ben Williamson <ben.williamson@greyinnovation.com> */ #include <linux/kernel.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/device.h>#include <linux/kfifo.h>#include <linux/spinlock.h> #include <sound/core.h>#include <sound/initval.h>#include <sound/rawmidi.h> #include <linux/usb/ch9.h>#include <linux/usb/func_utils.h>#include <linux/usb/gadget.h>#include <linux/usb/audio.h>#include <linux/usb/midi.h> #include "u_midi.h" MODULE_AUTHOR("Ben Williamson");MODULE_DESCRIPTION("USB MIDI class function driver");MODULE_LICENSE("GPL v2"); static const char f_midi_shortname[] = "f_midi";static const char f_midi_longname[] = "MIDI Gadget"; /* * We can only handle 16 cables on one single endpoint, as cable numbers are * stored in 4-bit fields. And as the interface currently only holds one * single endpoint, this is the maximum number of ports we can allow. */#define MAX_PORTS 16 /* MIDI message states */enum { STATE_INITIAL = 0, /* pseudo state */ STATE_1PARAM, STATE_2PARAM_1, STATE_2PARAM_2, STATE_SYSEX_0, STATE_SYSEX_1, STATE_SYSEX_2, STATE_REAL_TIME, STATE_FINISHED, /* pseudo state */}; /* * This is a gadget, and the IN/OUT naming is from the host's perspective. * USB -> OUT endpoint -> rawmidi * USB <- IN endpoint <- rawmidi */struct gmidi_in_port { struct snd_rawmidi_substream *substream; int active; uint8_t cable; uint8_t state; uint8_t data[2];}; struct f_midi { struct usb_function func; struct usb_gadget *gadget; struct usb_ep *in_ep, *out_ep; struct snd_card *card; struct snd_rawmidi *rmidi; u8 ms_id; struct snd_rawmidi_substream *out_substream[MAX_PORTS]; unsigned long out_triggered; struct work_struct work; unsigned int in_ports; unsigned int out_ports; int index; char *id; unsigned int buflen, qlen; /* This fifo is used as a buffer ring for pre-allocated IN usb_requests */ DECLARE_KFIFO_PTR(in_req_fifo, struct usb_request *); spinlock_t transmit_lock; unsigned int in_last_port; unsigned char free_ref; struct gmidi_in_port in_ports_array[] __counted_by(in_ports);}; static inline struct f_midi *func_to_midi(struct usb_function *f){ return container_of(f, struct f_midi, func);} static void f_midi_transmit(struct f_midi *midi);static void f_midi_rmidi_free(struct snd_rawmidi *rmidi);static void f_midi_free_inst(struct usb_function_instance *f); DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16); /* B.3.1 Standard AC Interface Descriptor */static struct usb_interface_descriptor ac_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ /* .bNumEndpoints = DYNAMIC */ .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, /* .iInterface = DYNAMIC */}; /* B.3.2 Class-Specific AC Interface Descriptor */static struct uac1_ac_header_descriptor_1 ac_header_desc = { .bLength = UAC_DT_AC_HEADER_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_HEADER, .bcdADC = cpu_to_le16(0x0100), .wTotalLength = cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)), .bInCollection = 1, /* .baInterfaceNr = DYNAMIC */}; /* B.4.1 Standard MS Interface Descriptor */static struct usb_interface_descriptor ms_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, /* .iInterface = DYNAMIC */}; /* B.4.2 Class-Specific MS Interface Descriptor */static struct usb_ms_header_descriptor ms_header_desc = { .bLength = USB_DT_MS_HEADER_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_HEADER, .bcdMSC = cpu_to_le16(0x0100), /* .wTotalLength = DYNAMIC */}; /* B.5.1 Standard Bulk OUT Endpoint Descriptor */static struct usb_endpoint_descriptor bulk_out_desc = { .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK,}; static struct usb_ss_ep_comp_descriptor bulk_out_ss_comp_desc = { .bLength = sizeof(bulk_out_ss_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /* .bMaxBurst = 0, */ /* .bmAttributes = 0, */}; /* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */static struct usb_ms_endpoint_descriptor_16 ms_out_desc = { /* .bLength = DYNAMIC */ .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubtype = USB_MS_GENERAL, /* .bNumEmbMIDIJack = DYNAMIC */ /* .baAssocJackID = DYNAMIC */}; /* B.6.1 Standard Bulk IN Endpoint Descriptor */static struct usb_endpoint_descriptor bulk_in_desc = { .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK,}; static struct usb_ss_ep_comp_descriptor bulk_in_ss_comp_desc = { .bLength = sizeof(bulk_in_ss_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /* .bMaxBurst = 0, */ /* .bmAttributes = 0, */}; /* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */static struct usb_ms_endpoint_descriptor_16 ms_in_desc = { /* .bLength = DYNAMIC */ .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubtype = USB_MS_GENERAL, /* .bNumEmbMIDIJack = DYNAMIC */ /* .baAssocJackID = DYNAMIC */}; /* string IDs are assigned dynamically */ #define STRING_FUNC_IDX 0 static struct usb_string midi_string_defs[] = { [STRING_FUNC_IDX].s = "MIDI function", { } /* end of list */}; static struct usb_gadget_strings midi_stringtab = { .language = 0x0409, /* en-us */ .strings = midi_string_defs,}; static struct usb_gadget_strings *midi_strings[] = { &midi_stringtab, NULL,}; static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep, unsigned length){ return alloc_ep_req(ep, length);} static const uint8_t f_midi_cin_length[] = { 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1}; /* * Receives a chunk of MIDI data. */static void f_midi_read_data(struct usb_ep *ep, int cable, uint8_t *data, int length){ struct f_midi *midi = ep->driver_data; struct snd_rawmidi_substream *substream = midi->out_substream[cable]; if (!substream) /* Nobody is listening - throw it on the floor. */ return; if (!test_bit(cable, &midi->out_triggered)) return; snd_rawmidi_receive(substream, data, length);} static void f_midi_handle_out_data(struct usb_ep *ep, struct usb_request *req){ unsigned int i; u8 *buf = req->buf; for (i = 0; i + 3 < req->actual; i += 4) if (buf[i] != 0) { int cable = buf[i] >> 4; int length = f_midi_cin_length[buf[i] & 0x0f]; f_midi_read_data(ep, cable, &buf[i + 1], length); }} static voidf_midi_complete(struct usb_ep *ep, struct usb_request *req){ struct f_midi *midi = ep->driver_data; struct usb_composite_dev *cdev = midi->func.config->cdev; int status = req->status; switch (status) { case 0: /* normal completion */ if (ep == midi->out_ep) { /* We received stuff. req is queued again, below */ f_midi_handle_out_data(ep, req); } else if (ep == midi->in_ep) { /* Our transmit completed. See if there's more to go. * f_midi_transmit eats req, don't queue it again. */ req->length = 0; queue_work(system_highpri_wq, &midi->work); return; } break; /* this endpoint is normally active while we're configured */ case -ECONNABORTED: /* hardware forced ep reset */ case -ECONNRESET: /* request dequeued */ case -ESHUTDOWN: /* disconnect from host */ VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status, req->actual, req->length); if (ep == midi->out_ep) { f_midi_handle_out_data(ep, req); /* We don't need to free IN requests because it's handled * by the midi->in_req_fifo. */ free_ep_req(ep, req); } return; case -EOVERFLOW: /* buffer overrun on read means that * we didn't provide a big enough buffer. */ default: DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name, status, req->actual, req->length); break; case -EREMOTEIO: /* short read */ break; } status = usb_ep_queue(ep, req, GFP_ATOMIC); if (status) { ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n", ep->name, req->length, status); usb_ep_set_halt(ep); /* FIXME recover later ... somehow */ }} static void f_midi_drop_out_substreams(struct f_midi *midi){ unsigned int i; for (i = 0; i < midi->in_ports; i++) { struct gmidi_in_port *port = midi->in_ports_array + i; struct snd_rawmidi_substream *substream = port->substream; if (port->active && substream) snd_rawmidi_drop_output(substream); }} static int f_midi_start_ep(struct f_midi *midi, struct usb_function *f, struct usb_ep *ep){ int err; struct usb_composite_dev *cdev = f->config->cdev; usb_ep_disable(ep); err = config_ep_by_speed(midi->gadget, f, ep); if (err) { ERROR(cdev, "can't configure %s: %d\n", ep->name, err); return err; } err = usb_ep_enable(ep); if (err) { ERROR(cdev, "can't start %s: %d\n", ep->name, err); return err; } ep->driver_data = midi; return 0;} static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt){ struct f_midi *midi = func_to_midi(f); unsigned i; int err; /* we only set alt for MIDIStreaming interface */ if (intf != midi->ms_id) return 0; err = f_midi_start_ep(midi, f, midi->in_ep); if (err) return err; err = f_midi_start_ep(midi, f, midi->out_ep); if (err) return err; /* pre-allocate write usb requests to use on f_midi_transmit. */ while (kfifo_avail(&midi->in_req_fifo)) { struct usb_request *req = midi_alloc_ep_req(midi->in_ep, midi->buflen); if (req == NULL) return -ENOMEM; req->length = 0; req->complete = f_midi_complete; kfifo_put(&midi->in_req_fifo, req); } /* allocate a bunch of read buffers and queue them all at once. */ for (i = 0; i < midi->qlen && err == 0; i++) { struct usb_request *req = midi_alloc_ep_req(midi->out_ep, midi->buflen); if (req == NULL) return -ENOMEM; req->complete = f_midi_complete; err = usb_ep_queue(midi->out_ep, req, GFP_ATOMIC); if (err) { ERROR(midi, "%s: couldn't enqueue request: %d\n", midi->out_ep->name, err); if (req->buf != NULL) free_ep_req(midi->out_ep, req); return err; } } return 0;} static void f_midi_disable(struct usb_function *f){ struct f_midi *midi = func_to_midi(f); struct usb_composite_dev *cdev = f->config->cdev; struct usb_request *req = NULL; DBG(cdev, "disable\n"); /* * just disable endpoints, forcing completion of pending i/o. * all our completion handlers free their requests in this case. */ usb_ep_disable(midi->in_ep); usb_ep_disable(midi->out_ep); /* release IN requests */ while (kfifo_get(&midi->in_req_fifo, &req)) free_ep_req(midi->in_ep, req); f_midi_drop_out_substreams(midi);} static int f_midi_snd_free(struct snd_device *device){ return 0;} /* * Converts MIDI commands to USB MIDI packets. */static void f_midi_transmit_byte(struct usb_request *req, struct gmidi_in_port *port, uint8_t b){ uint8_t p[4] = { port->cable << 4, 0, 0, 0 }; uint8_t next_state = STATE_INITIAL; switch (b) { case 0xf8 ... 0xff: /* System Real-Time Messages */ p[0] |= 0x0f; p[1] = b; next_state = port->state; port->state = STATE_REAL_TIME; break; case 0xf7: /* End of SysEx */ switch (port->state) { case STATE_SYSEX_0: p[0] |= 0x05; p[1] = 0xf7; next_state = STATE_FINISHED; break; case STATE_SYSEX_1: p[0] |= 0x06; p[1] = port->data[0]; p[2] = 0xf7; next_state = STATE_FINISHED; break; case STATE_SYSEX_2: p[0] |= 0x07; p[1] = port->data[0]; p[2] = port->data[1]; p[3] = 0xf7; next_state = STATE_FINISHED; break; default: /* Ignore byte */ next_state = port->state; port->state = STATE_INITIAL; } break; case 0xf0 ... 0xf6: /* System Common Messages */ port->data[0] = port->data[1] = 0; port->state = STATE_INITIAL; switch (b) { case 0xf0: port->data[0] = b; port->data[1] = 0; next_state = STATE_SYSEX_1; break;Lines 1–500 of 1415. Load more
// SPDX-License-Identifier: GPL-2.0+ /* * f_midi.c -- USB MIDI class function driver * * Copyright (C) 2006 Thumtronics Pty Ltd. * Developed for Thumtronics by Grey Innovation * Ben Williamson <ben.williamson@greyinnovation.com> * * Rewritten for the composite framework * Copyright (C) 2011 Daniel Mack <zonque@gmail.com> * * Based on drivers/usb/gadget/f_audio.c, * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> * Copyright (C) 2008 Analog Devices, Inc * * and drivers/usb/gadget/midi.c, * Copyright (C) 2006 Thumtronics Pty Ltd. * Ben Williamson <ben.williamson@greyinnovation.com> */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/device.h> #include <linux/kfifo.h> #include <linux/spinlock.h> #include <sound/core.h> #include <sound/initval.h> #include <sound/rawmidi.h> #include <linux/usb/ch9.h> #include <linux/usb/func_utils.h> #include <linux/usb/gadget.h> #include <linux/usb/audio.h> #include <linux/usb/midi.h> #include "u_midi.h" MODULE_AUTHOR("Ben Williamson"); MODULE_DESCRIPTION("USB MIDI class function driver"); MODULE_LICENSE("GPL v2"); static const char f_midi_shortname[] = "f_midi"; static const char f_midi_longname[] = "MIDI Gadget"; /* * We can only handle 16 cables on one single endpoint, as cable numbers are * stored in 4-bit fields. And as the interface currently only holds one * single endpoint, this is the maximum number of ports we can allow. */ #define MAX_PORTS 16 /* MIDI message states */ enum { STATE_INITIAL = 0, /* pseudo state */ STATE_1PARAM, STATE_2PARAM_1, STATE_2PARAM_2, STATE_SYSEX_0, STATE_SYSEX_1, STATE_SYSEX_2, STATE_REAL_TIME, STATE_FINISHED, /* pseudo state */ }; /* * This is a gadget, and the IN/OUT naming is from the host's perspective. * USB -> OUT endpoint -> rawmidi * USB <- IN endpoint <- rawmidi */ struct gmidi_in_port { struct snd_rawmidi_substream *substream; int active; uint8_t cable; uint8_t state; uint8_t data[2]; }; struct f_midi { struct usb_function func; struct usb_gadget *gadget; struct usb_ep *in_ep, *out_ep; struct snd_card *card; struct snd_rawmidi *rmidi; u8 ms_id; struct snd_rawmidi_substream *out_substream[MAX_PORTS]; unsigned long out_triggered; struct work_struct work; unsigned int in_ports; unsigned int out_ports; int index; char *id; unsigned int buflen, qlen; /* This fifo is used as a buffer ring for pre-allocated IN usb_requests */ DECLARE_KFIFO_PTR(in_req_fifo, struct usb_request *); spinlock_t transmit_lock; unsigned int in_last_port; unsigned char free_ref; struct gmidi_in_port in_ports_array[] __counted_by(in_ports); }; static inline struct f_midi *func_to_midi(struct usb_function *f) { return container_of(f, struct f_midi, func); } static void f_midi_transmit(struct f_midi *midi); static void f_midi_rmidi_free(struct snd_rawmidi *rmidi); static void f_midi_free_inst(struct usb_function_instance *f); DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16); /* B.3.1 Standard AC Interface Descriptor */ static struct usb_interface_descriptor ac_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ /* .bNumEndpoints = DYNAMIC */ .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, /* .iInterface = DYNAMIC */ }; /* B.3.2 Class-Specific AC Interface Descriptor */ static struct uac1_ac_header_descriptor_1 ac_header_desc = { .bLength = UAC_DT_AC_HEADER_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_HEADER, .bcdADC = cpu_to_le16(0x0100), .wTotalLength = cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)), .bInCollection = 1, /* .baInterfaceNr = DYNAMIC */ }; /* B.4.1 Standard MS Interface Descriptor */ static struct usb_interface_descriptor ms_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, /* .iInterface = DYNAMIC */ }; /* B.4.2 Class-Specific MS Interface Descriptor */ static struct usb_ms_header_descriptor ms_header_desc = { .bLength = USB_DT_MS_HEADER_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_HEADER, .bcdMSC = cpu_to_le16(0x0100), /* .wTotalLength = DYNAMIC */ }; /* B.5.1 Standard Bulk OUT Endpoint Descriptor */ static struct usb_endpoint_descriptor bulk_out_desc = { .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, }; static struct usb_ss_ep_comp_descriptor bulk_out_ss_comp_desc = { .bLength = sizeof(bulk_out_ss_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /* .bMaxBurst = 0, */ /* .bmAttributes = 0, */ }; /* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */ static struct usb_ms_endpoint_descriptor_16 ms_out_desc = { /* .bLength = DYNAMIC */ .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubtype = USB_MS_GENERAL, /* .bNumEmbMIDIJack = DYNAMIC */ /* .baAssocJackID = DYNAMIC */ }; /* B.6.1 Standard Bulk IN Endpoint Descriptor */ static struct usb_endpoint_descriptor bulk_in_desc = { .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, }; static struct usb_ss_ep_comp_descriptor bulk_in_ss_comp_desc = { .bLength = sizeof(bulk_in_ss_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /* .bMaxBurst = 0, */ /* .bmAttributes = 0, */ }; /* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */ static struct usb_ms_endpoint_descriptor_16 ms_in_desc = { /* .bLength = DYNAMIC */ .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubtype = USB_MS_GENERAL, /* .bNumEmbMIDIJack = DYNAMIC */ /* .baAssocJackID = DYNAMIC */ }; /* string IDs are assigned dynamically */ #define STRING_FUNC_IDX 0 static struct usb_string midi_string_defs[] = { [STRING_FUNC_IDX].s = "MIDI function", { } /* end of list */ }; static struct usb_gadget_strings midi_stringtab = { .language = 0x0409, /* en-us */ .strings = midi_string_defs, }; static struct usb_gadget_strings *midi_strings[] = { &midi_stringtab, NULL, }; static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep, unsigned length) { return alloc_ep_req(ep, length); } static const uint8_t f_midi_cin_length[] = { 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 }; /* * Receives a chunk of MIDI data. */ static void f_midi_read_data(struct usb_ep *ep, int cable, uint8_t *data, int length) { struct f_midi *midi = ep->driver_data; struct snd_rawmidi_substream *substream = midi->out_substream[cable]; if (!substream) /* Nobody is listening - throw it on the floor. */ return; if (!test_bit(cable, &midi->out_triggered)) return; snd_rawmidi_receive(substream, data, length); } static void f_midi_handle_out_data(struct usb_ep *ep, struct usb_request *req) { unsigned int i; u8 *buf = req->buf; for (i = 0; i + 3 < req->actual; i += 4) if (buf[i] != 0) { int cable = buf[i] >> 4; int length = f_midi_cin_length[buf[i] & 0x0f]; f_midi_read_data(ep, cable, &buf[i + 1], length); } } static void f_midi_complete(struct usb_ep *ep, struct usb_request *req) { struct f_midi *midi = ep->driver_data; struct usb_composite_dev *cdev = midi->func.config->cdev; int status = req->status; switch (status) { case 0: /* normal completion */ if (ep == midi->out_ep) { /* We received stuff. req is queued again, below */ f_midi_handle_out_data(ep, req); } else if (ep == midi->in_ep) { /* Our transmit completed. See if there's more to go. * f_midi_transmit eats req, don't queue it again. */ req->length = 0; queue_work(system_highpri_wq, &midi->work); return; } break; /* this endpoint is normally active while we're configured */ case -ECONNABORTED: /* hardware forced ep reset */ case -ECONNRESET: /* request dequeued */ case -ESHUTDOWN: /* disconnect from host */ VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status, req->actual, req->length); if (ep == midi->out_ep) { f_midi_handle_out_data(ep, req); /* We don't need to free IN requests because it's handled * by the midi->in_req_fifo. */ free_ep_req(ep, req); } return; case -EOVERFLOW: /* buffer overrun on read means that * we didn't provide a big enough buffer. */ default: DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name, status, req->actual, req->length); break; case -EREMOTEIO: /* short read */ break; } status = usb_ep_queue(ep, req, GFP_ATOMIC); if (status) { ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n", ep->name, req->length, status); usb_ep_set_halt(ep); /* FIXME recover later ... somehow */ } } static void f_midi_drop_out_substreams(struct f_midi *midi) { unsigned int i; for (i = 0; i < midi->in_ports; i++) { struct gmidi_in_port *port = midi->in_ports_array + i; struct snd_rawmidi_substream *substream = port->substream; if (port->active && substream) snd_rawmidi_drop_output(substream); } } static int f_midi_start_ep(struct f_midi *midi, struct usb_function *f, struct usb_ep *ep) { int err; struct usb_composite_dev *cdev = f->config->cdev; usb_ep_disable(ep); err = config_ep_by_speed(midi->gadget, f, ep); if (err) { ERROR(cdev, "can't configure %s: %d\n", ep->name, err); return err; } err = usb_ep_enable(ep); if (err) { ERROR(cdev, "can't start %s: %d\n", ep->name, err); return err; } ep->driver_data = midi; return 0; } static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct f_midi *midi = func_to_midi(f); unsigned i; int err; /* we only set alt for MIDIStreaming interface */ if (intf != midi->ms_id) return 0; err = f_midi_start_ep(midi, f, midi->in_ep); if (err) return err; err = f_midi_start_ep(midi, f, midi->out_ep); if (err) return err; /* pre-allocate write usb requests to use on f_midi_transmit. */ while (kfifo_avail(&midi->in_req_fifo)) { struct usb_request *req = midi_alloc_ep_req(midi->in_ep, midi->buflen); if (req == NULL) return -ENOMEM; req->length = 0; req->complete = f_midi_complete; kfifo_put(&midi->in_req_fifo, req); } /* allocate a bunch of read buffers and queue them all at once. */ for (i = 0; i < midi->qlen && err == 0; i++) { struct usb_request *req = midi_alloc_ep_req(midi->out_ep, midi->buflen); if (req == NULL) return -ENOMEM; req->complete = f_midi_complete; err = usb_ep_queue(midi->out_ep, req, GFP_ATOMIC); if (err) { ERROR(midi, "%s: couldn't enqueue request: %d\n", midi->out_ep->name, err); if (req->buf != NULL) free_ep_req(midi->out_ep, req); return err; } } return 0; } static void f_midi_disable(struct usb_function *f) { struct f_midi *midi = func_to_midi(f); struct usb_composite_dev *cdev = f->config->cdev; struct usb_request *req = NULL; DBG(cdev, "disable\n"); /* * just disable endpoints, forcing completion of pending i/o. * all our completion handlers free their requests in this case. */ usb_ep_disable(midi->in_ep); usb_ep_disable(midi->out_ep); /* release IN requests */ while (kfifo_get(&midi->in_req_fifo, &req)) free_ep_req(midi->in_ep, req); f_midi_drop_out_substreams(midi); } static int f_midi_snd_free(struct snd_device *device) { return 0; } /* * Converts MIDI commands to USB MIDI packets. */ static void f_midi_transmit_byte(struct usb_request *req, struct gmidi_in_port *port, uint8_t b) { uint8_t p[4] = { port->cable << 4, 0, 0, 0 }; uint8_t next_state = STATE_INITIAL; switch (b) { case 0xf8 ... 0xff: /* System Real-Time Messages */ p[0] |= 0x0f; p[1] = b; next_state = port->state; port->state = STATE_REAL_TIME; break; case 0xf7: /* End of SysEx */ switch (port->state) { case STATE_SYSEX_0: p[0] |= 0x05; p[1] = 0xf7; next_state = STATE_FINISHED; break; case STATE_SYSEX_1: p[0] |= 0x06; p[1] = port->data[0]; p[2] = 0xf7; next_state = STATE_FINISHED; break; case STATE_SYSEX_2: p[0] |= 0x07; p[1] = port->data[0]; p[2] = port->data[1]; p[3] = 0xf7; next_state = STATE_FINISHED; break; default: /* Ignore byte */ next_state = port->state; port->state = STATE_INITIAL; } break; case 0xf0 ... 0xf6: /* System Common Messages */ port->data[0] = port->data[1] = 0; port->state = STATE_INITIAL; switch (b) { case 0xf0: port->data[0] = b; port->data[1] = 0; next_state = STATE_SYSEX_1; break;