2893 lines · 73.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* * f_midi2.c -- USB MIDI 2.0 class function driver */ #include <linux/device.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/slab.h> #include <sound/core.h>#include <sound/control.h>#include <sound/ump.h>#include <sound/ump_msg.h>#include <sound/ump_convert.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-v2.h> #include "u_midi2.h" struct f_midi2;struct f_midi2_ep;struct f_midi2_usb_ep; /* Context for each USB request */struct f_midi2_req_ctx {	struct f_midi2_usb_ep *usb_ep;	/* belonging USB EP */	unsigned int index;		/* array index: 0-31 */	struct usb_request *req;	/* assigned request */}; /* Resources for a USB Endpoint */struct f_midi2_usb_ep {	struct f_midi2 *card;		/* belonging card */	struct f_midi2_ep *ep;		/* belonging UMP EP (optional) */	struct usb_ep *usb_ep;		/* assigned USB EP */	void (*complete)(struct usb_ep *usb_ep, struct usb_request *req);	unsigned long free_reqs;	/* bitmap for unused requests */	unsigned int num_reqs;		/* number of allocated requests */	struct f_midi2_req_ctx *reqs;	/* request context array */}; /* Resources for UMP Function Block (and USB Group Terminal Block) */struct f_midi2_block {	struct f_midi2_block_info info;	/* FB info, copied from configfs */	struct snd_ump_block *fb;	/* assigned FB */	unsigned int gtb_id;		/* assigned GTB id */	unsigned int string_id;		/* assigned string id */}; /* Temporary buffer for altset 0 MIDI 1.0 handling */struct f_midi2_midi1_port {	unsigned int pending; /* pending bytes on the input buffer */	u8 buf[32];	/* raw MIDI 1.0 byte input */	u8 state;	/* running status */	u8 data[2];	/* rendered USB MIDI 1.0 packet data */}; /* MIDI 1.0 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 */}; /* Resources for UMP Endpoint */struct f_midi2_ep {	struct snd_ump_endpoint *ump;	/* assigned UMP EP */	struct f_midi2 *card;		/* belonging MIDI 2.0 device */ 	struct f_midi2_ep_info info;	/* UMP EP info, copied from configfs */	unsigned int num_blks;		/* number of FBs */	struct f_midi2_block blks[SNDRV_UMP_MAX_BLOCKS];	/* UMP FBs */ 	struct f_midi2_usb_ep ep_in;	/* USB MIDI EP-in */	struct f_midi2_usb_ep ep_out;	/* USB MIDI EP-out */ 	u8 in_group_to_cable[SNDRV_UMP_MAX_GROUPS]; /* map to cable; 1-based! */}; /* indices for USB strings */enum {	STR_IFACE = 0,	STR_GTB1 = 1,}; /* 1-based GTB id to string id */#define gtb_to_str_id(id)	(STR_GTB1 + (id) - 1) /* mapping from MIDI 1.0 cable to UMP group */struct midi1_cable_mapping {	struct f_midi2_ep *ep;	unsigned char block;	unsigned char group;}; /* operation mode */enum {	MIDI_OP_MODE_UNSET,	/* no altset set yet */	MIDI_OP_MODE_MIDI1,	/* MIDI 1.0 (altset 0) is used */	MIDI_OP_MODE_MIDI2,	/* MIDI 2.0 (altset 1) is used */}; /* Resources for MIDI 2.0 Device */struct f_midi2 {	struct usb_function func;	struct usb_gadget *gadget;	struct snd_card *card; 	/* MIDI 1.0 in/out USB EPs */	struct f_midi2_usb_ep midi1_ep_in;	struct f_midi2_usb_ep midi1_ep_out; 	/* number of MIDI 1.0 I/O cables */	unsigned int num_midi1_in;	unsigned int num_midi1_out; 	/* conversion for MIDI 1.0 EP-in */	struct f_midi2_midi1_port midi1_port[MAX_CABLES];	/* conversion for MIDI 1.0 EP-out */	struct ump_cvt_to_ump midi1_ump_cvt;	/* mapping between cables and UMP groups */	struct midi1_cable_mapping in_cable_mapping[MAX_CABLES];	struct midi1_cable_mapping out_cable_mapping[MAX_CABLES]; 	int midi_if;			/* USB MIDI interface number */	int operation_mode;		/* current operation mode */ 	spinlock_t queue_lock; 	struct f_midi2_card_info info;	/* card info, copied from configfs */ 	unsigned int num_eps;	struct f_midi2_ep midi2_eps[MAX_UMP_EPS]; 	unsigned int total_blocks;	/* total number of blocks of all EPs */	struct usb_string *string_defs;	struct usb_string *strings;}; #define func_to_midi2(f)	container_of(f, struct f_midi2, func) /* convert from MIDI protocol number (1 or 2) to SNDRV_UMP_EP_INFO_PROTO_* */#define to_ump_protocol(v)	(((v) & 3) << 8) /* get EP name string */static const char *ump_ep_name(const struct f_midi2_ep *ep){	return ep->info.ep_name ? ep->info.ep_name : "MIDI 2.0 Gadget";} /* get EP product ID string */static const char *ump_product_id(const struct f_midi2_ep *ep){	return ep->info.product_id ? ep->info.product_id : "Unique Product ID";} /* get FB name string */static const char *ump_fb_name(const struct f_midi2_block_info *info){	return info->name ? info->name : "MIDI 2.0 Gadget I/O";} /* * USB Descriptor Definitions *//* GTB header descriptor */static struct usb_ms20_gr_trm_block_header_descriptor gtb_header_desc = {	.bLength =		sizeof(gtb_header_desc),	.bDescriptorType =	USB_DT_CS_GR_TRM_BLOCK,	.bDescriptorSubtype =	USB_MS_GR_TRM_BLOCK_HEADER,	.wTotalLength =		__cpu_to_le16(0x12), // to be filled}; /* GTB descriptor template: most items are replaced dynamically */static struct usb_ms20_gr_trm_block_descriptor gtb_desc = {	.bLength =		sizeof(gtb_desc),	.bDescriptorType =	USB_DT_CS_GR_TRM_BLOCK,	.bDescriptorSubtype =	USB_MS_GR_TRM_BLOCK,	.bGrpTrmBlkID =		0x01,	.bGrpTrmBlkType =	USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL,	.nGroupTrm =		0x00,	.nNumGroupTrm =		1,	.iBlockItem =		0,	.bMIDIProtocol =	USB_MS_MIDI_PROTO_1_0_64,	.wMaxInputBandwidth =	0,	.wMaxOutputBandwidth =	0,}; DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16);DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);DECLARE_USB_MS20_ENDPOINT_DESCRIPTOR(32); #define EP_MAX_PACKET_INT	8 /* Audio Control Interface */static struct usb_interface_descriptor midi2_audio_if_desc = {	.bLength =		USB_DT_INTERFACE_SIZE,	.bDescriptorType =	USB_DT_INTERFACE,	.bInterfaceNumber =	0, // to be filled	.bNumEndpoints =	0,	.bInterfaceClass =	USB_CLASS_AUDIO,	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOCONTROL,	.bInterfaceProtocol =	0,	.iInterface =		0,}; static struct uac1_ac_header_descriptor_1 midi2_audio_class_desc = {	.bLength =		0x09,	.bDescriptorType =	USB_DT_CS_INTERFACE,	.bDescriptorSubtype =	0x01,	.bcdADC =		__cpu_to_le16(0x0100),	.wTotalLength =		__cpu_to_le16(0x0009),	.bInCollection =	0x01,	.baInterfaceNr =	{ 0x01 }, // to be filled}; /* MIDI 1.0 Streaming Interface (altset 0) */static struct usb_interface_descriptor midi2_midi1_if_desc = {	.bLength =		USB_DT_INTERFACE_SIZE,	.bDescriptorType =	USB_DT_INTERFACE,	.bInterfaceNumber =	0, // to be filled	.bAlternateSetting =	0,	.bNumEndpoints =	2, // to be filled	.bInterfaceClass =	USB_CLASS_AUDIO,	.bInterfaceSubClass =	USB_SUBCLASS_MIDISTREAMING,	.bInterfaceProtocol =	0,	.iInterface =		0, // to be filled}; static struct usb_ms_header_descriptor midi2_midi1_class_desc = {	.bLength =		0x07,	.bDescriptorType =	USB_DT_CS_INTERFACE,	.bDescriptorSubtype =	USB_MS_HEADER,	.bcdMSC =		__cpu_to_le16(0x0100),	.wTotalLength =		__cpu_to_le16(0x41), // to be calculated}; /* MIDI 1.0 EP OUT */static struct usb_endpoint_descriptor midi2_midi1_ep_out_desc = {	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,	.bDescriptorType =	USB_DT_ENDPOINT,	.bEndpointAddress =	USB_DIR_OUT | 0, // set up dynamically	.bmAttributes =		USB_ENDPOINT_XFER_BULK,}; static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_out_ss_comp_desc = {	.bLength                = sizeof(midi2_midi1_ep_out_ss_comp_desc),	.bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,}; static struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_out_class_desc = {	.bLength =		0x05, // to be filled	.bDescriptorType =	USB_DT_CS_ENDPOINT,	.bDescriptorSubtype =	USB_MS_GENERAL,	.bNumEmbMIDIJack =	1,	.baAssocJackID =	{ 0x01 },}; /* MIDI 1.0 EP IN */static struct usb_endpoint_descriptor midi2_midi1_ep_in_desc = {	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,	.bDescriptorType =	USB_DT_ENDPOINT,	.bEndpointAddress =	USB_DIR_IN | 0, // set up dynamically	.bmAttributes =		USB_ENDPOINT_XFER_BULK,}; static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_in_ss_comp_desc = {	.bLength                = sizeof(midi2_midi1_ep_in_ss_comp_desc),	.bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,}; static struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_in_class_desc = {	.bLength =		0x05, // to be filled	.bDescriptorType =	USB_DT_CS_ENDPOINT,	.bDescriptorSubtype =	USB_MS_GENERAL,	.bNumEmbMIDIJack =	1,	.baAssocJackID =	{ 0x03 },}; /* MIDI 2.0 Streaming Interface (altset 1) */static struct usb_interface_descriptor midi2_midi2_if_desc = {	.bLength =		USB_DT_INTERFACE_SIZE,	.bDescriptorType =	USB_DT_INTERFACE,	.bInterfaceNumber =	0, // to be filled	.bAlternateSetting =	1,	.bNumEndpoints =	2, // to be filled	.bInterfaceClass =	USB_CLASS_AUDIO,	.bInterfaceSubClass =	USB_SUBCLASS_MIDISTREAMING,	.bInterfaceProtocol =	0,	.iInterface =		0, // to be filled}; static struct usb_ms_header_descriptor midi2_midi2_class_desc = {	.bLength =		0x07,	.bDescriptorType =	USB_DT_CS_INTERFACE,	.bDescriptorSubtype =	USB_MS_HEADER,	.bcdMSC =		__cpu_to_le16(0x0200),	.wTotalLength =		__cpu_to_le16(0x07),}; /* MIDI 2.0 EP OUT */static struct usb_endpoint_descriptor midi2_midi2_ep_out_desc[MAX_UMP_EPS]; static struct usb_ss_ep_comp_descriptor midi2_midi2_ep_out_ss_comp_desc = {	.bLength                = sizeof(midi2_midi1_ep_out_ss_comp_desc),	.bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,}; static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_out_class_desc[MAX_UMP_EPS]; /* MIDI 2.0 EP IN */static struct usb_endpoint_descriptor midi2_midi2_ep_in_desc[MAX_UMP_EPS]; static struct usb_ss_ep_comp_descriptor midi2_midi2_ep_in_ss_comp_desc = {	.bLength                = sizeof(midi2_midi2_ep_in_ss_comp_desc),	.bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,}; static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_in_class_desc[MAX_UMP_EPS]; /* Arrays of descriptors to be created */static void *midi2_audio_descs[] = {	&midi2_audio_if_desc,	&midi2_audio_class_desc,	NULL}; static void *midi2_midi1_descs[] = {	&midi2_midi1_if_desc,	&midi2_midi1_class_desc,	NULL}; static void *midi2_midi1_ep_out_descs[] = {	&midi2_midi1_ep_out_desc,	&midi2_midi1_ep_out_class_desc,	NULL}; static void *midi2_midi1_ep_in_descs[] = {	&midi2_midi1_ep_in_desc,	&midi2_midi1_ep_in_class_desc,	NULL}; static void *midi2_midi1_ep_out_ss_descs[] = {	&midi2_midi1_ep_out_desc,	&midi2_midi1_ep_out_ss_comp_desc,	&midi2_midi1_ep_out_class_desc,	NULL}; static void *midi2_midi1_ep_in_ss_descs[] = {	&midi2_midi1_ep_in_desc,	&midi2_midi1_ep_in_ss_comp_desc,	&midi2_midi1_ep_in_class_desc,	NULL}; static void *midi2_midi2_descs[] = {	&midi2_midi2_if_desc,	&midi2_midi2_class_desc,	NULL}; /* * USB request handling */ /* get an empty request for the given EP */static struct usb_request *get_empty_request(struct f_midi2_usb_ep *usb_ep){	struct usb_request *req = NULL;	unsigned long flags;	int index; 	spin_lock_irqsave(&usb_ep->card->queue_lock, flags);	if (!usb_ep->free_reqs)		goto unlock;	index = find_first_bit(&usb_ep->free_reqs, usb_ep->num_reqs);	if (index >= usb_ep->num_reqs)		goto unlock;	req = usb_ep->reqs[index].req;	if (!req)		goto unlock;	clear_bit(index, &usb_ep->free_reqs);	req->length = 0; unlock:	spin_unlock_irqrestore(&usb_ep->card->queue_lock, flags);	return req;} /* put the empty request back */static void put_empty_request(struct usb_request *req){	struct f_midi2_req_ctx *ctx = req->context;	unsigned long flags; 	spin_lock_irqsave(&ctx->usb_ep->card->queue_lock, flags);	set_bit(ctx->index, &ctx->usb_ep->free_reqs);	spin_unlock_irqrestore(&ctx->usb_ep->card->queue_lock, flags);} /* * UMP v1.1 Stream message handling */ /* queue a request to UMP EP; request is either queued or freed after this */static int queue_request_ep_raw(struct usb_request *req){	struct f_midi2_req_ctx *ctx = req->context;	int err; 	req->complete = ctx->usb_ep->complete;	err = usb_ep_queue(ctx->usb_ep->usb_ep, req, GFP_ATOMIC);	if (err) {		put_empty_request(req);		return err;	}	return 0;} /* queue a request with endianness conversion */static int queue_request_ep_in(struct usb_request *req){	/* UMP packets have to be converted to little-endian */	cpu_to_le32_array((u32 *)req->buf, req->length >> 2);	return queue_request_ep_raw(req);} /* reply a UMP packet via EP-in */static int reply_ep_in(struct f_midi2_ep *ep, const void *buf, int len){	struct f_midi2_usb_ep *usb_ep = &ep->ep_in;	struct usb_request *req; 	req = get_empty_request(usb_ep);	if (!req)		return -ENOSPC; 	req->length = len;	memcpy(req->buf, buf, len);	return queue_request_ep_in(req);} /* reply a UMP stream EP info */static void reply_ump_stream_ep_info(struct f_midi2_ep *ep){	struct snd_ump_stream_msg_ep_info rep = {		.type = UMP_MSG_TYPE_STREAM,		.status = UMP_STREAM_MSG_STATUS_EP_INFO,		.ump_version_major = 0x01,		.ump_version_minor = 0x01,		.num_function_blocks = ep->num_blks,		.static_function_block = !!ep->card->info.static_block,		.protocol = (UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 |			     UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) >> 8,	}; 	reply_ep_in(ep, &rep, sizeof(rep));} /* reply a UMP EP device info */static void reply_ump_stream_ep_device(struct f_midi2_ep *ep){	struct snd_ump_stream_msg_device_info rep = {		.type = UMP_MSG_TYPE_STREAM,		.status = UMP_STREAM_MSG_STATUS_DEVICE_INFO,		.manufacture_id = ep->info.manufacturer,		.family_lsb = ep->info.family & 0xff,		.family_msb = (ep->info.family >> 8) & 0xff,		.model_lsb = ep->info.model & 0xff,		.model_msb = (ep->info.model >> 8) & 0xff,		.sw_revision = ep->info.sw_revision,	}; 	reply_ep_in(ep, &rep, sizeof(rep));} #define UMP_STREAM_PKT_BYTES	16	/* UMP stream packet size = 16 bytes*/#define UMP_STREAM_EP_STR_OFF	2	/* offset of name string for EP info */#define UMP_STREAM_FB_STR_OFF	3	/* offset of name string for FB info */ /* Helper to replay a string */static void reply_ump_stream_string(struct f_midi2_ep *ep, const u8 *name,				    unsigned int type, unsigned int extra,				    unsigned int start_ofs){

Lines 1–500 of 2893.