// 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. Load more
// 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) {