// SPDX-License-Identifier: GPL-2.0-or-later
/*
* MIDI 2.0 support
*/
#include <linux/bitops.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/wait.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/usb/audio.h>
#include <linux/usb/midi.h>
#include <linux/usb/midi-v2.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/ump.h>
#include "usbaudio.h"
#include "midi.h"
#include "midi2.h"
#include "helper.h"
static bool midi2_enable = true;
module_param(midi2_enable, bool, 0444);
MODULE_PARM_DESC(midi2_enable, "Enable MIDI 2.0 support.");
static bool midi2_ump_probe = true;
module_param(midi2_ump_probe, bool, 0444);
MODULE_PARM_DESC(midi2_ump_probe, "Probe UMP v1.1 support at first.");
/* stream direction; just shorter names */
enum {
STR_OUT = SNDRV_RAWMIDI_STREAM_OUTPUT,
STR_IN = SNDRV_RAWMIDI_STREAM_INPUT
};
#define NUM_URBS 8
struct snd_usb_midi2_urb;
struct snd_usb_midi2_endpoint;
struct snd_usb_midi2_ump;
struct snd_usb_midi2_interface;
/* URB context */
struct snd_usb_midi2_urb {
struct urb *urb;
struct snd_usb_midi2_endpoint *ep;
unsigned int index; /* array index */
};
/* A USB MIDI input/output endpoint */
struct snd_usb_midi2_endpoint {
struct usb_device *dev;
const struct usb_ms20_endpoint_descriptor *ms_ep; /* reference to EP descriptor */
struct snd_usb_midi2_endpoint *pair; /* bidirectional pair EP */
struct snd_usb_midi2_ump *rmidi; /* assigned UMP EP pair */
struct snd_ump_endpoint *ump; /* assigned UMP EP */
int direction; /* direction (STR_IN/OUT) */
unsigned int endpoint; /* EP number */
unsigned int pipe; /* URB pipe */
unsigned int packets; /* packet buffer size in bytes */
unsigned int interval; /* interval for INT EP */
wait_queue_head_t wait; /* URB waiter */
spinlock_t lock; /* URB locking */
struct snd_rawmidi_substream *substream; /* NULL when closed */
unsigned int num_urbs; /* number of allocated URBs */
unsigned long urb_free; /* bitmap for free URBs */
unsigned long urb_free_mask; /* bitmask for free URBs */
atomic_t running; /* running status */
atomic_t suspended; /* saved running status for suspend */
bool disconnected; /* shadow of umidi->disconnected */
struct list_head list; /* list to umidi->ep_list */
struct snd_usb_midi2_urb urbs[NUM_URBS];
};
/* A UMP endpoint - one or two USB MIDI endpoints are assigned */
struct snd_usb_midi2_ump {
struct usb_device *dev;
struct snd_usb_midi2_interface *umidi; /* reference to MIDI iface */
struct snd_ump_endpoint *ump; /* assigned UMP EP object */
struct snd_usb_midi2_endpoint *eps[2]; /* USB MIDI endpoints */
int index; /* rawmidi device index */
unsigned char usb_block_id; /* USB GTB id used for finding a pair */
bool ump_parsed; /* Parsed UMP 1.1 EP/FB info*/
struct list_head list; /* list to umidi->rawmidi_list */
};
/* top-level instance per USB MIDI interface */
struct snd_usb_midi2_interface {
struct snd_usb_audio *chip; /* assigned USB-audio card */
struct usb_interface *iface; /* assigned USB interface */
struct usb_host_interface *hostif;
const char *blk_descs; /* group terminal block descriptors */
unsigned int blk_desc_size; /* size of GTB descriptors */
bool disconnected;
struct list_head ep_list; /* list of endpoints */
struct list_head rawmidi_list; /* list of UMP rawmidis */
struct list_head list; /* list to chip->midi_v2_list */
};
/* submit URBs as much as possible; used for both input and output */
static void do_submit_urbs_locked(struct snd_usb_midi2_endpoint *ep,
int (*prepare)(struct snd_usb_midi2_endpoint *,
struct urb *))
{
struct snd_usb_midi2_urb *ctx;
int index, err = 0;
if (ep->disconnected)
return;
while (ep->urb_free) {
index = find_first_bit(&ep->urb_free, ep->num_urbs);
if (index >= ep->num_urbs)
return;
ctx = &ep->urbs[index];
err = prepare(e