linux/drivers/usb/gadget/function/f_midi2.c

// 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 {};

/* Resources for a USB Endpoint */
struct f_midi2_usb_ep {};

/* Resources for UMP Function Block (and USB Group Terminal Block) */
struct f_midi2_block {};

/* Temporary buffer for altset 0 MIDI 1.0 handling */
struct f_midi2_midi1_port {};

/* MIDI 1.0 message states */
enum {};

/* Resources for UMP Endpoint */
struct f_midi2_ep {};

/* indices for USB strings */
enum {};

/* 1-based GTB id to string id */
#define gtb_to_str_id(id)

/* mapping from MIDI 1.0 cable to UMP group */
struct midi1_cable_mapping {};

/* operation mode */
enum {};

/* Resources for MIDI 2.0 Device */
struct f_midi2 {};

#define func_to_midi2(f)

/* convert from MIDI protocol number (1 or 2) to SNDRV_UMP_EP_INFO_PROTO_* */
#define to_ump_protocol(v)

/* get EP name string */
static const char *ump_ep_name(const struct f_midi2_ep *ep)
{}

/* get EP product ID string */
static const char *ump_product_id(const struct f_midi2_ep *ep)
{}

/* get FB name string */
static const char *ump_fb_name(const struct f_midi2_block_info *info)
{}

/*
 * USB Descriptor Definitions
 */
/* GTB header descriptor */
static struct usb_ms20_gr_trm_block_header_descriptor gtb_header_desc =;

/* GTB descriptor template: most items are replaced dynamically */
static struct usb_ms20_gr_trm_block_descriptor gtb_desc =;

DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR} ;
DECLARE_USB_MS_ENDPOINT_DESCRIPTOR} ;
DECLARE_UAC_AC_HEADER_DESCRIPTOR} ;
DECLARE_USB_MS20_ENDPOINT_DESCRIPTOR} ;

#define EP_MAX_PACKET_INT

/* Audio Control Interface */
static struct usb_interface_descriptor midi2_audio_if_desc =;

static struct uac1_ac_header_descriptor_1 midi2_audio_class_desc =;

/* MIDI 1.0 Streaming Interface (altset 0) */
static struct usb_interface_descriptor midi2_midi1_if_desc =;

static struct usb_ms_header_descriptor midi2_midi1_class_desc =;

/* MIDI 1.0 EP OUT */
static struct usb_endpoint_descriptor midi2_midi1_ep_out_desc =;

static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_out_ss_comp_desc =;

static struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_out_class_desc =;

/* MIDI 1.0 EP IN */
static struct usb_endpoint_descriptor midi2_midi1_ep_in_desc =;

static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_in_ss_comp_desc =;

static struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_in_class_desc =;

/* MIDI 2.0 Streaming Interface (altset 1) */
static struct usb_interface_descriptor midi2_midi2_if_desc =;

static struct usb_ms_header_descriptor midi2_midi2_class_desc =;

/* 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 =;

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 =;

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[] =;

static void *midi2_midi1_descs[] =;

static void *midi2_midi1_ep_out_descs[] =;

static void *midi2_midi1_ep_in_descs[] =;

static void *midi2_midi1_ep_out_ss_descs[] =;

static void *midi2_midi1_ep_in_ss_descs[] =;

static void *midi2_midi2_descs[] =;

/*
 * 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)
{}

/* put the empty request back */
static void put_empty_request(struct usb_request *req)
{}

/*
 * 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)
{}

/* queue a request with endianness conversion */
static int queue_request_ep_in(struct usb_request *req)
{}

/* reply a UMP packet via EP-in */
static int reply_ep_in(struct f_midi2_ep *ep, const void *buf, int len)
{}

/* reply a UMP stream EP info */
static void reply_ump_stream_ep_info(struct f_midi2_ep *ep)
{}

/* reply a UMP EP device info */
static void reply_ump_stream_ep_device(struct f_midi2_ep *ep)
{}

#define UMP_STREAM_PKT_BYTES
#define UMP_STREAM_EP_STR_OFF
#define UMP_STREAM_FB_STR_OFF

/* 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)
{}

/* Reply a UMP EP name string */
static void reply_ump_stream_ep_name(struct f_midi2_ep *ep)
{}

/* Reply a UMP EP product ID string */
static void reply_ump_stream_ep_pid(struct f_midi2_ep *ep)
{}

/* Reply a UMP EP stream config */
static void reply_ump_stream_ep_config(struct f_midi2_ep *ep)
{}

/* Reply a UMP FB info */
static void reply_ump_stream_fb_info(struct f_midi2_ep *ep, int blk)
{}

/* Reply a FB name string */
static void reply_ump_stream_fb_name(struct f_midi2_ep *ep, unsigned int blk)
{}

/* Process a UMP Stream message */
static void process_ump_stream_msg(struct f_midi2_ep *ep, const u32 *data)
{}

/* Process UMP messages included in a USB request */
static void process_ump(struct f_midi2_ep *ep, const struct usb_request *req)
{}

/*
 * MIDI 2.0 UMP USB request handling
 */

/* complete handler for UMP EP-out requests */
static void f_midi2_ep_out_complete(struct usb_ep *usb_ep,
				    struct usb_request *req)
{}

/* Transmit UMP packets received from user-space to the gadget */
static void process_ump_transmit(struct f_midi2_ep *ep)
{}

/* Complete handler for UMP EP-in requests */
static void f_midi2_ep_in_complete(struct usb_ep *usb_ep,
				   struct usb_request *req)
{}

/*
 * MIDI1 (altset 0) USB request handling
 */

/* process one MIDI byte -- copied from f_midi.c
 *
 * fill the packet or request if needed
 * returns true if the request became empty (queued)
 */
static bool process_midi1_byte(struct f_midi2 *midi2, u8 cable, u8 b,
			       struct usb_request **req_p)
{}

/* process all pending MIDI bytes in the internal buffer;
 * returns true if the request gets empty
 * returns false if all have been processed
 */
static bool process_midi1_pending_buf(struct f_midi2 *midi2,
				      struct usb_request **req_p)
{}

/* fill the MIDI bytes onto the temporary buffer
 */
static void fill_midi1_pending_buf(struct f_midi2 *midi2, u8 cable, u8 *buf,
				   unsigned int size)
{}

/* try to process data given from the associated UMP stream */
static void process_midi1_transmit(struct f_midi2 *midi2)
{}

/* complete handler for MIDI1 EP-in requests */
static void f_midi2_midi1_ep_in_complete(struct usb_ep *usb_ep,
					 struct usb_request *req)
{}

/* complete handler for MIDI1 EP-out requests */
static void f_midi2_midi1_ep_out_complete(struct usb_ep *usb_ep,
					  struct usb_request *req)
{}

/*
 * Common EP handling helpers
 */

/* Start MIDI EP */
static int f_midi2_start_ep(struct f_midi2_usb_ep *usb_ep,
			    struct usb_function *fn)
{}

/* Drop pending requests */
static void f_midi2_drop_reqs(struct f_midi2_usb_ep *usb_ep)
{}

/* Allocate requests for the given EP */
static int f_midi2_alloc_ep_reqs(struct f_midi2_usb_ep *usb_ep)
{}

/* Free allocated requests */
static void f_midi2_free_ep_reqs(struct f_midi2_usb_ep *usb_ep)
{}

/* Initialize EP */
static int f_midi2_init_ep(struct f_midi2 *midi2, struct f_midi2_ep *ep,
			   struct f_midi2_usb_ep *usb_ep,
			   void *desc,
			   void (*complete)(struct usb_ep *usb_ep,
					    struct usb_request *req))
{}

/* Free EP */
static void f_midi2_free_ep(struct f_midi2_usb_ep *usb_ep)
{}

/* Queue requests for EP-out at start */
static void f_midi2_queue_out_reqs(struct f_midi2_usb_ep *usb_ep)
{}

/*
 * Gadget Function callbacks
 */

/* stop both IN and OUT EPs */
static void f_midi2_stop_eps(struct f_midi2_usb_ep *ep_in,
			     struct f_midi2_usb_ep *ep_out)
{}

/* start/queue both IN and OUT EPs */
static int f_midi2_start_eps(struct f_midi2_usb_ep *ep_in,
			     struct f_midi2_usb_ep *ep_out,
			     struct usb_function *fn)
{}

/* gadget function set_alt callback */
static int f_midi2_set_alt(struct usb_function *fn, unsigned int intf,
			   unsigned int alt)
{}

/* gadget function get_alt callback */
static int f_midi2_get_alt(struct usb_function *fn, unsigned int intf)
{}

/* convert UMP direction to USB MIDI 2.0 direction */
static unsigned int ump_to_usb_dir(unsigned int ump_dir)
{}

/* assign GTB descriptors (for the given request) */
static void assign_block_descriptors(struct f_midi2 *midi2,
				     struct usb_request *req,
				     int max_len)
{}

/* gadget function setup callback: handle GTB requests */
static int f_midi2_setup(struct usb_function *fn,
			 const struct usb_ctrlrequest *ctrl)
{}

/* gadget function disable callback */
static void f_midi2_disable(struct usb_function *fn)
{}

/*
 * ALSA UMP ops: most of them are NOPs, only trigger for write is needed
 */
static int f_midi2_ump_open(struct snd_ump_endpoint *ump, int dir)
{}

static void f_midi2_ump_close(struct snd_ump_endpoint *ump, int dir)
{}

static void f_midi2_ump_trigger(struct snd_ump_endpoint *ump, int dir, int up)
{}

static void f_midi2_ump_drain(struct snd_ump_endpoint *ump, int dir)
{}

static const struct snd_ump_ops f_midi2_ump_ops =;

/*
 * "Operation Mode" control element
 */
static int f_midi2_operation_mode_info(struct snd_kcontrol *kcontrol,
				       struct snd_ctl_elem_info *uinfo)
{}

static int f_midi2_operation_mode_get(struct snd_kcontrol *kcontrol,
				      struct snd_ctl_elem_value *ucontrol)
{}

static const struct snd_kcontrol_new operation_mode_ctl =;

/*
 * ALSA UMP instance creation / deletion
 */
static void f_midi2_free_card(struct f_midi2 *midi2)
{}

/* use a reverse direction for the gadget host */
static int reverse_dir(int dir)
{}

static int f_midi2_create_card(struct f_midi2 *midi2)
{}

/*
 * Creation of USB descriptors
 */
struct f_midi2_usb_config {};

static int append_config(struct f_midi2_usb_config *config, void *d)
{}

static int append_configs(struct f_midi2_usb_config *config, void **d)
{}

static int append_midi1_in_jack(struct f_midi2 *midi2,
				struct f_midi2_usb_config *config,
				struct midi1_cable_mapping *map,
				unsigned int type)
{}

static int append_midi1_out_jack(struct f_midi2 *midi2,
				 struct f_midi2_usb_config *config,
				 struct midi1_cable_mapping *map,
				 unsigned int type, unsigned int source)
{}

static int f_midi2_create_usb_configs(struct f_midi2 *midi2,
				      struct f_midi2_usb_config *config,
				      int speed)
{}

static void f_midi2_free_usb_configs(struct f_midi2_usb_config *config)
{}

/* as we use the static descriptors for simplicity, serialize bind call */
static DEFINE_MUTEX(f_midi2_desc_mutex);

/* fill MIDI2 EP class-specific descriptor */
static void fill_midi2_class_desc(struct f_midi2_ep *ep,
				  struct usb_ms20_endpoint_descriptor_32 *cdesc)
{}

/* initialize MIDI2 EP-in */
static int f_midi2_init_midi2_ep_in(struct f_midi2 *midi2, int index)
{}

/* initialize MIDI2 EP-out */
static int f_midi2_init_midi2_ep_out(struct f_midi2 *midi2, int index)
{}

/* gadget function bind callback */
static int f_midi2_bind(struct usb_configuration *c, struct usb_function *f)
{}

/* gadget function unbind callback */
static void f_midi2_unbind(struct usb_configuration *c, struct usb_function *f)
{}

/*
 * ConfigFS interface
 */

/* type conversion helpers */
static inline struct f_midi2_opts *to_f_midi2_opts(struct config_item *item)
{}

static inline struct f_midi2_ep_opts *
to_f_midi2_ep_opts(struct config_item *item)
{}

static inline struct f_midi2_block_opts *
to_f_midi2_block_opts(struct config_item *item)
{}

/* trim the string to be usable for EP and FB name strings */
static void make_name_string(char *s)
{}

/* configfs helpers: generic show/store for unisnged int */
static ssize_t f_midi2_opts_uint_show(struct f_midi2_opts *opts,
				      u32 val, const char *format, char *page)
{}

static ssize_t f_midi2_opts_uint_store(struct f_midi2_opts *opts,
				       u32 *valp, u32 minval, u32 maxval,
				       const char *page, size_t len)
{}

/* generic store for bool */
static ssize_t f_midi2_opts_bool_store(struct f_midi2_opts *opts,
				       bool *valp, const char *page, size_t len)
{}

/* generic show/store for string */
static ssize_t f_midi2_opts_str_show(struct f_midi2_opts *opts,
				     const char *str, char *page)
{}

static ssize_t f_midi2_opts_str_store(struct f_midi2_opts *opts,
				      const char **strp, size_t maxlen,
				      const char *page, size_t len)
{}

/*
 * Definitions for UMP Block config
 */

/* define an uint option for block */
#define F_MIDI2_BLOCK_OPT(name, format, minval, maxval)

/* define a boolean option for block */
#define F_MIDI2_BLOCK_BOOL_OPT(name)

F_MIDI2_BLOCK_OPT();
F_MIDI2_BLOCK_OPT();
F_MIDI2_BLOCK_OPT();
F_MIDI2_BLOCK_OPT();
F_MIDI2_BLOCK_OPT();
F_MIDI2_BLOCK_OPT();
F_MIDI2_BLOCK_OPT();
F_MIDI2_BLOCK_OPT();
F_MIDI2_BLOCK_OPT();
F_MIDI2_BLOCK_BOOL_OPT();

static ssize_t f_midi2_block_opts_name_show(struct config_item *item,
					    char *page)
{}

static ssize_t f_midi2_block_opts_name_store(struct config_item *item,
					     const char *page, size_t len)
{}

CONFIGFS_ATTR();

static struct configfs_attribute *f_midi2_block_attrs[] =;

static void f_midi2_block_opts_release(struct config_item *item)
{}

static struct configfs_item_operations f_midi2_block_item_ops =;

static const struct config_item_type f_midi2_block_type =;

/* create a f_midi2_block_opts instance for the given block number */
static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts,
				     unsigned int blk,
				     struct f_midi2_block_opts **block_p)
{}

/* make_group callback for a block */
static struct config_group *
f_midi2_opts_block_make(struct config_group *group, const char *name)
{}

/* drop_item callback for a block */
static void
f_midi2_opts_block_drop(struct config_group *group, struct config_item *item)
{}

/*
 * Definitions for UMP Endpoint config
 */

/* define an uint option for EP */
#define F_MIDI2_EP_OPT(name, format, minval, maxval)

/* define a string option for EP */
#define F_MIDI2_EP_STR_OPT(name, maxlen)

F_MIDI2_EP_OPT();
F_MIDI2_EP_OPT();
F_MIDI2_EP_OPT();
F_MIDI2_EP_OPT();
F_MIDI2_EP_OPT();
F_MIDI2_EP_OPT();
F_MIDI2_EP_STR_OPT();
F_MIDI2_EP_STR_OPT();

static struct configfs_attribute *f_midi2_ep_attrs[] =;

static void f_midi2_ep_opts_release(struct config_item *item)
{}

static struct configfs_item_operations f_midi2_ep_item_ops =;

static struct configfs_group_operations f_midi2_ep_group_ops =;

static const struct config_item_type f_midi2_ep_type =;

/* create a f_midi2_ep_opts instance */
static int f_midi2_ep_opts_create(struct f_midi2_opts *opts,
				  unsigned int index,
				  struct f_midi2_ep_opts **ep_p)
{}

/* make_group callback for an EP */
static struct config_group *
f_midi2_opts_ep_make(struct config_group *group, const char *name)
{}

/* drop_item callback for an EP */
static void
f_midi2_opts_ep_drop(struct config_group *group, struct config_item *item)
{}

/*
 * Definitions for card config
 */

/* define a bool option for card */
#define F_MIDI2_BOOL_OPT(name)

F_MIDI2_BOOL_OPT();
F_MIDI2_BOOL_OPT();

static ssize_t f_midi2_opts_iface_name_show(struct config_item *item,
					    char *page)
{}

static ssize_t f_midi2_opts_iface_name_store(struct config_item *item,
					     const char *page, size_t len)
{}

CONFIGFS_ATTR();

static struct configfs_attribute *f_midi2_attrs[] =;

static void f_midi2_opts_release(struct config_item *item)
{}

static struct configfs_item_operations f_midi2_item_ops =;

static struct configfs_group_operations f_midi2_group_ops =;

static const struct config_item_type f_midi2_func_type =;

static void f_midi2_free_inst(struct usb_function_instance *f)
{}

/* gadget alloc_inst */
static struct usb_function_instance *f_midi2_alloc_inst(void)
{}

static void do_f_midi2_free(struct f_midi2 *midi2, struct f_midi2_opts *opts)
{}

static void f_midi2_free(struct usb_function *f)
{}

/* verify the parameters set up via configfs;
 * return the number of EPs or a negative error
 */
static int verify_parameters(struct f_midi2_opts *opts)
{}

/* fill mapping between MIDI 1.0 cable and UMP EP/group */
static void fill_midi1_cable_mapping(struct f_midi2 *midi2,
				     struct f_midi2_ep *ep,
				     int blk)
{}

/* gadget alloc callback */
static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
{}

DECLARE_USB_FUNCTION_INIT(midi2, f_midi2_alloc_inst, f_midi2_alloc);

MODULE_DESCRIPTION();
MODULE_LICENSE();