1415 lines · 35.5 KB · C
RawDownload
// 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.