Newer
Older
// 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*/
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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(ep, ctx->urb);
if (err < 0)
return;
if (!ctx->urb->transfer_buffer_length)
return;
ctx->urb->dev = ep->dev;
err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
if (err < 0) {
dev_dbg(&ep->dev->dev,
"usb_submit_urb error %d\n", err);
return;
}
clear_bit(index, &ep->urb_free);
}
}
/* prepare for output submission: copy from rawmidi buffer to urb packet */
static int prepare_output_urb(struct snd_usb_midi2_endpoint *ep,
struct urb *urb)
{
int count;
count = snd_ump_transmit(ep->ump, urb->transfer_buffer,
ep->packets);
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
if (count < 0) {
dev_dbg(&ep->dev->dev, "rawmidi transmit error %d\n", count);
return count;
}
cpu_to_le32_array((u32 *)urb->transfer_buffer, count >> 2);
urb->transfer_buffer_length = count;
return 0;
}
static void submit_output_urbs_locked(struct snd_usb_midi2_endpoint *ep)
{
do_submit_urbs_locked(ep, prepare_output_urb);
}
/* URB completion for output; re-filling and re-submit */
static void output_urb_complete(struct urb *urb)
{
struct snd_usb_midi2_urb *ctx = urb->context;
struct snd_usb_midi2_endpoint *ep = ctx->ep;
unsigned long flags;
spin_lock_irqsave(&ep->lock, flags);
set_bit(ctx->index, &ep->urb_free);
if (urb->status >= 0 && atomic_read(&ep->running))
submit_output_urbs_locked(ep);
if (ep->urb_free == ep->urb_free_mask)
wake_up(&ep->wait);
spin_unlock_irqrestore(&ep->lock, flags);
}
/* prepare for input submission: just set the buffer length */
static int prepare_input_urb(struct snd_usb_midi2_endpoint *ep,
struct urb *urb)
{
urb->transfer_buffer_length = ep->packets;
return 0;
}
static void submit_input_urbs_locked(struct snd_usb_midi2_endpoint *ep)
{
do_submit_urbs_locked(ep, prepare_input_urb);
}
/* URB completion for input; copy into rawmidi buffer and resubmit */
static void input_urb_complete(struct urb *urb)
{
struct snd_usb_midi2_urb *ctx = urb->context;
struct snd_usb_midi2_endpoint *ep = ctx->ep;
unsigned long flags;
int len;
spin_lock_irqsave(&ep->lock, flags);
if (ep->disconnected || urb->status < 0)
goto dequeue;
len = urb->actual_length;
len &= ~3; /* align UMP */
if (len > ep->packets)
Loading
Loading full blame...