]>
Commit | Line | Data |
---|---|---|
ff49d1df TI |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * MIDI 2.0 support | |
4 | */ | |
5 | ||
6 | #include <linux/bitops.h> | |
7 | #include <linux/string.h> | |
8 | #include <linux/init.h> | |
9 | #include <linux/slab.h> | |
10 | #include <linux/usb.h> | |
11 | #include <linux/wait.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/moduleparam.h> | |
14 | #include <linux/usb/audio.h> | |
15 | #include <linux/usb/midi.h> | |
16 | #include <linux/usb/midi-v2.h> | |
17 | ||
18 | #include <sound/core.h> | |
19 | #include <sound/control.h> | |
20 | #include <sound/ump.h> | |
21 | #include "usbaudio.h" | |
22 | #include "midi.h" | |
23 | #include "midi2.h" | |
24 | #include "helper.h" | |
25 | ||
26 | static bool midi2_enable = true; | |
27 | module_param(midi2_enable, bool, 0444); | |
28 | MODULE_PARM_DESC(midi2_enable, "Enable MIDI 2.0 support."); | |
29 | ||
30 | /* stream direction; just shorter names */ | |
31 | enum { | |
32 | STR_OUT = SNDRV_RAWMIDI_STREAM_OUTPUT, | |
33 | STR_IN = SNDRV_RAWMIDI_STREAM_INPUT | |
34 | }; | |
35 | ||
36 | #define NUM_URBS 8 | |
37 | ||
38 | struct snd_usb_midi2_urb; | |
39 | struct snd_usb_midi2_endpoint; | |
40 | struct snd_usb_midi2_ump; | |
41 | struct snd_usb_midi2_interface; | |
42 | ||
43 | /* URB context */ | |
44 | struct snd_usb_midi2_urb { | |
45 | struct urb *urb; | |
46 | struct snd_usb_midi2_endpoint *ep; | |
47 | unsigned int index; /* array index */ | |
48 | }; | |
49 | ||
50 | /* A USB MIDI input/output endpoint */ | |
51 | struct snd_usb_midi2_endpoint { | |
52 | struct usb_device *dev; | |
53 | const struct usb_ms20_endpoint_descriptor *ms_ep; /* reference to EP descriptor */ | |
54 | struct snd_usb_midi2_endpoint *pair; /* bidirectional pair EP */ | |
55 | struct snd_usb_midi2_ump *rmidi; /* assigned UMP EP */ | |
56 | int direction; /* direction (STR_IN/OUT) */ | |
57 | unsigned int endpoint; /* EP number */ | |
58 | unsigned int pipe; /* URB pipe */ | |
59 | unsigned int packets; /* packet buffer size in bytes */ | |
60 | unsigned int interval; /* interval for INT EP */ | |
61 | wait_queue_head_t wait; /* URB waiter */ | |
62 | spinlock_t lock; /* URB locking */ | |
63 | struct snd_rawmidi_substream *substream; /* NULL when closed */ | |
64 | unsigned int num_urbs; /* number of allocated URBs */ | |
65 | unsigned long urb_free; /* bitmap for free URBs */ | |
66 | unsigned long urb_free_mask; /* bitmask for free URBs */ | |
67 | atomic_t running; /* running status */ | |
68 | atomic_t suspended; /* saved running status for suspend */ | |
69 | bool disconnected; /* shadow of umidi->disconnected */ | |
70 | struct list_head list; /* list to umidi->ep_list */ | |
71 | struct snd_usb_midi2_urb urbs[NUM_URBS]; | |
72 | }; | |
73 | ||
74 | /* A UMP endpoint - one or two USB MIDI endpoints are assigned */ | |
75 | struct snd_usb_midi2_ump { | |
76 | struct usb_device *dev; | |
77 | struct snd_usb_midi2_interface *umidi; /* reference to MIDI iface */ | |
78 | struct snd_ump_endpoint *ump; /* assigned UMP EP object */ | |
79 | struct snd_usb_midi2_endpoint *eps[2]; /* USB MIDI endpoints */ | |
80 | int index; /* rawmidi device index */ | |
81 | unsigned char usb_block_id; /* USB GTB id used for finding a pair */ | |
82 | struct list_head list; /* list to umidi->rawmidi_list */ | |
83 | }; | |
84 | ||
85 | /* top-level instance per USB MIDI interface */ | |
86 | struct snd_usb_midi2_interface { | |
87 | struct snd_usb_audio *chip; /* assigned USB-audio card */ | |
88 | struct usb_interface *iface; /* assigned USB interface */ | |
89 | struct usb_host_interface *hostif; | |
90 | const char *blk_descs; /* group terminal block descriptors */ | |
91 | unsigned int blk_desc_size; /* size of GTB descriptors */ | |
92 | bool disconnected; | |
93 | struct list_head ep_list; /* list of endpoints */ | |
94 | struct list_head rawmidi_list; /* list of UMP rawmidis */ | |
95 | struct list_head list; /* list to chip->midi_v2_list */ | |
96 | }; | |
97 | ||
98 | /* submit URBs as much as possible; used for both input and output */ | |
99 | static void do_submit_urbs_locked(struct snd_usb_midi2_endpoint *ep, | |
100 | int (*prepare)(struct snd_usb_midi2_endpoint *, | |
101 | struct urb *)) | |
102 | { | |
103 | struct snd_usb_midi2_urb *ctx; | |
104 | int index, err = 0; | |
105 | ||
106 | if (ep->disconnected) | |
107 | return; | |
108 | ||
109 | while (ep->urb_free) { | |
110 | index = find_first_bit(&ep->urb_free, ep->num_urbs); | |
111 | if (index >= ep->num_urbs) | |
112 | return; | |
113 | ctx = &ep->urbs[index]; | |
114 | err = prepare(ep, ctx->urb); | |
115 | if (err < 0) | |
116 | return; | |
117 | if (!ctx->urb->transfer_buffer_length) | |
118 | return; | |
119 | ctx->urb->dev = ep->dev; | |
120 | err = usb_submit_urb(ctx->urb, GFP_ATOMIC); | |
121 | if (err < 0) { | |
122 | dev_dbg(&ep->dev->dev, | |
123 | "usb_submit_urb error %d\n", err); | |
124 | return; | |
125 | } | |
126 | clear_bit(index, &ep->urb_free); | |
127 | } | |
128 | } | |
129 | ||
130 | /* prepare for output submission: copy from rawmidi buffer to urb packet */ | |
131 | static int prepare_output_urb(struct snd_usb_midi2_endpoint *ep, | |
132 | struct urb *urb) | |
133 | { | |
134 | int count; | |
135 | ||
136 | if (ep->substream) | |
137 | count = snd_rawmidi_transmit(ep->substream, | |
138 | urb->transfer_buffer, | |
139 | ep->packets); | |
140 | else | |
141 | count = -ENODEV; | |
142 | if (count < 0) { | |
143 | dev_dbg(&ep->dev->dev, "rawmidi transmit error %d\n", count); | |
144 | return count; | |
145 | } | |
146 | cpu_to_le32_array((u32 *)urb->transfer_buffer, count >> 2); | |
147 | urb->transfer_buffer_length = count; | |
148 | return 0; | |
149 | } | |
150 | ||
151 | static void submit_output_urbs_locked(struct snd_usb_midi2_endpoint *ep) | |
152 | { | |
153 | do_submit_urbs_locked(ep, prepare_output_urb); | |
154 | } | |
155 | ||
156 | /* URB completion for output; re-filling and re-submit */ | |
157 | static void output_urb_complete(struct urb *urb) | |
158 | { | |
159 | struct snd_usb_midi2_urb *ctx = urb->context; | |
160 | struct snd_usb_midi2_endpoint *ep = ctx->ep; | |
161 | unsigned long flags; | |
162 | ||
163 | spin_lock_irqsave(&ep->lock, flags); | |
164 | set_bit(ctx->index, &ep->urb_free); | |
165 | if (urb->status >= 0 && atomic_read(&ep->running)) | |
166 | submit_output_urbs_locked(ep); | |
167 | if (ep->urb_free == ep->urb_free_mask) | |
168 | wake_up(&ep->wait); | |
169 | spin_unlock_irqrestore(&ep->lock, flags); | |
170 | } | |
171 | ||
172 | /* prepare for input submission: just set the buffer length */ | |
173 | static int prepare_input_urb(struct snd_usb_midi2_endpoint *ep, | |
174 | struct urb *urb) | |
175 | { | |
176 | urb->transfer_buffer_length = ep->packets; | |
177 | return 0; | |
178 | } | |
179 | ||
180 | static void submit_input_urbs_locked(struct snd_usb_midi2_endpoint *ep) | |
181 | { | |
182 | do_submit_urbs_locked(ep, prepare_input_urb); | |
183 | } | |
184 | ||
185 | /* URB completion for input; copy into rawmidi buffer and resubmit */ | |
186 | static void input_urb_complete(struct urb *urb) | |
187 | { | |
188 | struct snd_usb_midi2_urb *ctx = urb->context; | |
189 | struct snd_usb_midi2_endpoint *ep = ctx->ep; | |
190 | unsigned long flags; | |
191 | int len; | |
192 | ||
193 | spin_lock_irqsave(&ep->lock, flags); | |
194 | if (ep->disconnected || urb->status < 0) | |
195 | goto dequeue; | |
196 | len = urb->actual_length; | |
197 | len &= ~3; /* align UMP */ | |
198 | if (len > ep->packets) | |
199 | len = ep->packets; | |
200 | if (len > 0 && ep->substream) { | |
201 | le32_to_cpu_array((u32 *)urb->transfer_buffer, len >> 2); | |
202 | snd_rawmidi_receive(ep->substream, urb->transfer_buffer, len); | |
203 | } | |
204 | dequeue: | |
205 | set_bit(ctx->index, &ep->urb_free); | |
206 | submit_input_urbs_locked(ep); | |
207 | if (ep->urb_free == ep->urb_free_mask) | |
208 | wake_up(&ep->wait); | |
209 | spin_unlock_irqrestore(&ep->lock, flags); | |
210 | } | |
211 | ||
212 | /* URB submission helper; for both direction */ | |
213 | static void submit_io_urbs(struct snd_usb_midi2_endpoint *ep) | |
214 | { | |
215 | unsigned long flags; | |
216 | ||
217 | if (!ep) | |
218 | return; | |
219 | spin_lock_irqsave(&ep->lock, flags); | |
220 | if (ep->direction == STR_IN) | |
221 | submit_input_urbs_locked(ep); | |
222 | else | |
223 | submit_output_urbs_locked(ep); | |
224 | spin_unlock_irqrestore(&ep->lock, flags); | |
225 | } | |
226 | ||
227 | /* kill URBs for close, suspend and disconnect */ | |
228 | static void kill_midi_urbs(struct snd_usb_midi2_endpoint *ep, bool suspending) | |
229 | { | |
230 | int i; | |
231 | ||
232 | if (!ep) | |
233 | return; | |
234 | if (suspending) | |
235 | ep->suspended = ep->running; | |
236 | atomic_set(&ep->running, 0); | |
237 | for (i = 0; i < ep->num_urbs; i++) { | |
238 | if (!ep->urbs[i].urb) | |
239 | break; | |
240 | usb_kill_urb(ep->urbs[i].urb); | |
241 | } | |
242 | } | |
243 | ||
244 | /* wait until all URBs get freed */ | |
245 | static void drain_urb_queue(struct snd_usb_midi2_endpoint *ep) | |
246 | { | |
247 | if (!ep) | |
248 | return; | |
249 | spin_lock_irq(&ep->lock); | |
250 | atomic_set(&ep->running, 0); | |
251 | wait_event_lock_irq_timeout(ep->wait, | |
252 | ep->disconnected || | |
253 | ep->urb_free == ep->urb_free_mask, | |
254 | ep->lock, msecs_to_jiffies(500)); | |
255 | spin_unlock_irq(&ep->lock); | |
256 | } | |
257 | ||
258 | /* release URBs for an EP */ | |
259 | static void free_midi_urbs(struct snd_usb_midi2_endpoint *ep) | |
260 | { | |
261 | struct snd_usb_midi2_urb *ctx; | |
262 | int i; | |
263 | ||
264 | if (!ep) | |
265 | return; | |
266 | for (i = 0; i < ep->num_urbs; ++i) { | |
267 | ctx = &ep->urbs[i]; | |
268 | if (!ctx->urb) | |
269 | break; | |
270 | usb_free_coherent(ep->dev, ep->packets, | |
271 | ctx->urb->transfer_buffer, | |
272 | ctx->urb->transfer_dma); | |
273 | usb_free_urb(ctx->urb); | |
274 | ctx->urb = NULL; | |
275 | } | |
276 | ep->num_urbs = 0; | |
277 | } | |
278 | ||
279 | /* allocate URBs for an EP */ | |
280 | static int alloc_midi_urbs(struct snd_usb_midi2_endpoint *ep) | |
281 | { | |
282 | struct snd_usb_midi2_urb *ctx; | |
283 | void (*comp)(struct urb *urb); | |
284 | void *buffer; | |
285 | int i, err; | |
286 | int endpoint, len; | |
287 | ||
288 | endpoint = ep->endpoint; | |
289 | len = ep->packets; | |
290 | if (ep->direction == STR_IN) | |
291 | comp = input_urb_complete; | |
292 | else | |
293 | comp = output_urb_complete; | |
294 | ||
295 | ep->num_urbs = 0; | |
296 | ep->urb_free = ep->urb_free_mask = 0; | |
297 | for (i = 0; i < NUM_URBS; i++) { | |
298 | ctx = &ep->urbs[i]; | |
299 | ctx->index = i; | |
300 | ctx->urb = usb_alloc_urb(0, GFP_KERNEL); | |
301 | if (!ctx->urb) { | |
302 | dev_err(&ep->dev->dev, "URB alloc failed\n"); | |
303 | return -ENOMEM; | |
304 | } | |
305 | ctx->ep = ep; | |
306 | buffer = usb_alloc_coherent(ep->dev, len, GFP_KERNEL, | |
307 | &ctx->urb->transfer_dma); | |
308 | if (!buffer) { | |
309 | dev_err(&ep->dev->dev, | |
310 | "URB buffer alloc failed (size %d)\n", len); | |
311 | return -ENOMEM; | |
312 | } | |
313 | if (ep->interval) | |
314 | usb_fill_int_urb(ctx->urb, ep->dev, ep->pipe, | |
315 | buffer, len, comp, ctx, ep->interval); | |
316 | else | |
317 | usb_fill_bulk_urb(ctx->urb, ep->dev, ep->pipe, | |
318 | buffer, len, comp, ctx); | |
319 | err = usb_urb_ep_type_check(ctx->urb); | |
320 | if (err < 0) { | |
321 | dev_err(&ep->dev->dev, "invalid MIDI EP %x\n", | |
322 | endpoint); | |
323 | return err; | |
324 | } | |
325 | ctx->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; | |
326 | ep->num_urbs++; | |
327 | } | |
328 | ep->urb_free = ep->urb_free_mask = GENMASK(ep->num_urbs - 1, 0); | |
329 | return 0; | |
330 | } | |
331 | ||
332 | static struct snd_usb_midi2_endpoint * | |
333 | substream_to_endpoint(struct snd_rawmidi_substream *substream) | |
334 | { | |
335 | struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi); | |
336 | struct snd_usb_midi2_ump *rmidi = ump->private_data; | |
337 | ||
338 | return rmidi->eps[substream->stream]; | |
339 | } | |
340 | ||
341 | /* rawmidi open callback */ | |
342 | static int snd_usb_midi_v2_open(struct snd_rawmidi_substream *substream) | |
343 | { | |
344 | struct snd_usb_midi2_endpoint *ep = substream_to_endpoint(substream); | |
345 | int err = 0; | |
346 | ||
347 | if (!ep || !ep->endpoint) | |
348 | return -ENODEV; | |
349 | if (ep->disconnected) | |
350 | return -EIO; | |
351 | if (ep->substream) | |
352 | return -EBUSY; | |
353 | if (ep->direction == STR_OUT) { | |
354 | err = alloc_midi_urbs(ep); | |
355 | if (err) | |
356 | return err; | |
357 | } | |
358 | spin_lock_irq(&ep->lock); | |
359 | ep->substream = substream; | |
360 | spin_unlock_irq(&ep->lock); | |
361 | return 0; | |
362 | } | |
363 | ||
364 | /* rawmidi close callback */ | |
365 | static int snd_usb_midi_v2_close(struct snd_rawmidi_substream *substream) | |
366 | { | |
367 | struct snd_usb_midi2_endpoint *ep = substream_to_endpoint(substream); | |
368 | ||
369 | spin_lock_irq(&ep->lock); | |
370 | ep->substream = NULL; | |
371 | spin_unlock_irq(&ep->lock); | |
372 | if (ep->direction == STR_OUT) { | |
373 | kill_midi_urbs(ep, false); | |
374 | drain_urb_queue(ep); | |
375 | free_midi_urbs(ep); | |
376 | } | |
377 | return 0; | |
378 | } | |
379 | ||
380 | /* rawmidi trigger callback */ | |
381 | static void snd_usb_midi_v2_trigger(struct snd_rawmidi_substream *substream, | |
382 | int up) | |
383 | { | |
384 | struct snd_usb_midi2_endpoint *ep = substream_to_endpoint(substream); | |
385 | ||
386 | atomic_set(&ep->running, up); | |
387 | if (up && ep->direction == STR_OUT && !ep->disconnected) | |
388 | submit_io_urbs(ep); | |
389 | } | |
390 | ||
391 | /* rawmidi drain callback */ | |
392 | static void snd_usb_midi_v2_drain(struct snd_rawmidi_substream *substream) | |
393 | { | |
394 | struct snd_usb_midi2_endpoint *ep = substream_to_endpoint(substream); | |
395 | ||
396 | drain_urb_queue(ep); | |
397 | } | |
398 | ||
399 | /* allocate and start all input streams */ | |
400 | static int start_input_streams(struct snd_usb_midi2_interface *umidi) | |
401 | { | |
402 | struct snd_usb_midi2_endpoint *ep; | |
403 | int err; | |
404 | ||
405 | list_for_each_entry(ep, &umidi->ep_list, list) { | |
406 | if (ep->direction == STR_IN) { | |
407 | err = alloc_midi_urbs(ep); | |
408 | if (err < 0) | |
409 | goto error; | |
410 | } | |
411 | } | |
412 | ||
413 | list_for_each_entry(ep, &umidi->ep_list, list) { | |
414 | if (ep->direction == STR_IN) | |
415 | submit_io_urbs(ep); | |
416 | } | |
417 | ||
418 | return 0; | |
419 | ||
420 | error: | |
421 | list_for_each_entry(ep, &umidi->ep_list, list) { | |
422 | if (ep->direction == STR_IN) | |
423 | free_midi_urbs(ep); | |
424 | } | |
425 | ||
426 | return err; | |
427 | } | |
428 | ||
429 | static const struct snd_rawmidi_ops output_ops = { | |
430 | .open = snd_usb_midi_v2_open, | |
431 | .close = snd_usb_midi_v2_close, | |
432 | .trigger = snd_usb_midi_v2_trigger, | |
433 | .drain = snd_usb_midi_v2_drain, | |
434 | }; | |
435 | ||
436 | static const struct snd_rawmidi_ops input_ops = { | |
437 | .open = snd_usb_midi_v2_open, | |
438 | .close = snd_usb_midi_v2_close, | |
439 | .trigger = snd_usb_midi_v2_trigger, | |
440 | }; | |
441 | ||
442 | /* create a USB MIDI 2.0 endpoint object */ | |
443 | static int create_midi2_endpoint(struct snd_usb_midi2_interface *umidi, | |
444 | struct usb_host_endpoint *hostep, | |
445 | const struct usb_ms20_endpoint_descriptor *ms_ep) | |
446 | { | |
447 | struct snd_usb_midi2_endpoint *ep; | |
448 | int endpoint, dir; | |
449 | ||
450 | usb_audio_dbg(umidi->chip, "Creating an EP 0x%02x, #GTB=%d\n", | |
451 | hostep->desc.bEndpointAddress, | |
452 | ms_ep->bNumGrpTrmBlock); | |
453 | ||
454 | ep = kzalloc(sizeof(*ep), GFP_KERNEL); | |
455 | if (!ep) | |
456 | return -ENOMEM; | |
457 | ||
458 | spin_lock_init(&ep->lock); | |
459 | init_waitqueue_head(&ep->wait); | |
460 | ep->dev = umidi->chip->dev; | |
461 | endpoint = hostep->desc.bEndpointAddress; | |
462 | dir = (endpoint & USB_DIR_IN) ? STR_IN : STR_OUT; | |
463 | ||
464 | ep->endpoint = endpoint; | |
465 | ep->direction = dir; | |
466 | ep->ms_ep = ms_ep; | |
467 | if (usb_endpoint_xfer_int(&hostep->desc)) | |
468 | ep->interval = hostep->desc.bInterval; | |
469 | else | |
470 | ep->interval = 0; | |
471 | if (dir == STR_IN) { | |
472 | if (ep->interval) | |
473 | ep->pipe = usb_rcvintpipe(ep->dev, endpoint); | |
474 | else | |
475 | ep->pipe = usb_rcvbulkpipe(ep->dev, endpoint); | |
476 | } else { | |
477 | if (ep->interval) | |
478 | ep->pipe = usb_sndintpipe(ep->dev, endpoint); | |
479 | else | |
480 | ep->pipe = usb_sndbulkpipe(ep->dev, endpoint); | |
481 | } | |
482 | ep->packets = usb_maxpacket(ep->dev, ep->pipe); | |
483 | list_add_tail(&ep->list, &umidi->ep_list); | |
484 | ||
485 | return 0; | |
486 | } | |
487 | ||
488 | /* destructor for endpoint; from snd_usb_midi_v2_free() */ | |
489 | static void free_midi2_endpoint(struct snd_usb_midi2_endpoint *ep) | |
490 | { | |
491 | list_del(&ep->list); | |
492 | free_midi_urbs(ep); | |
493 | kfree(ep); | |
494 | } | |
495 | ||
496 | /* call all endpoint destructors */ | |
497 | static void free_all_midi2_endpoints(struct snd_usb_midi2_interface *umidi) | |
498 | { | |
499 | struct snd_usb_midi2_endpoint *ep; | |
500 | ||
501 | while (!list_empty(&umidi->ep_list)) { | |
502 | ep = list_first_entry(&umidi->ep_list, | |
503 | struct snd_usb_midi2_endpoint, list); | |
504 | free_midi2_endpoint(ep); | |
505 | } | |
506 | } | |
507 | ||
508 | /* find a MIDI STREAMING descriptor with a given subtype */ | |
509 | static void *find_usb_ms_endpoint_descriptor(struct usb_host_endpoint *hostep, | |
510 | unsigned char subtype) | |
511 | { | |
512 | unsigned char *extra = hostep->extra; | |
513 | int extralen = hostep->extralen; | |
514 | ||
515 | while (extralen > 3) { | |
516 | struct usb_ms_endpoint_descriptor *ms_ep = | |
517 | (struct usb_ms_endpoint_descriptor *)extra; | |
518 | ||
519 | if (ms_ep->bLength > 3 && | |
520 | ms_ep->bDescriptorType == USB_DT_CS_ENDPOINT && | |
521 | ms_ep->bDescriptorSubtype == subtype) | |
522 | return ms_ep; | |
523 | if (!extra[0]) | |
524 | break; | |
525 | extralen -= extra[0]; | |
526 | extra += extra[0]; | |
527 | } | |
528 | return NULL; | |
529 | } | |
530 | ||
531 | /* get the full group terminal block descriptors and return the size */ | |
532 | static int get_group_terminal_block_descs(struct snd_usb_midi2_interface *umidi) | |
533 | { | |
534 | struct usb_host_interface *hostif = umidi->hostif; | |
535 | struct usb_device *dev = umidi->chip->dev; | |
536 | struct usb_ms20_gr_trm_block_header_descriptor header = { 0 }; | |
537 | unsigned char *data; | |
538 | int err, size; | |
539 | ||
540 | err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), | |
541 | USB_REQ_GET_DESCRIPTOR, | |
542 | USB_RECIP_INTERFACE | USB_TYPE_STANDARD | USB_DIR_IN, | |
543 | USB_DT_CS_GR_TRM_BLOCK << 8 | hostif->desc.bAlternateSetting, | |
544 | hostif->desc.bInterfaceNumber, | |
545 | &header, sizeof(header)); | |
546 | if (err < 0) | |
547 | return err; | |
548 | size = __le16_to_cpu(header.wTotalLength); | |
549 | if (!size) { | |
550 | dev_err(&dev->dev, "Failed to get GTB descriptors for %d:%d\n", | |
551 | hostif->desc.bInterfaceNumber, hostif->desc.bAlternateSetting); | |
552 | return -EINVAL; | |
553 | } | |
554 | ||
555 | data = kzalloc(size, GFP_KERNEL); | |
556 | if (!data) | |
557 | return -ENOMEM; | |
558 | ||
559 | err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), | |
560 | USB_REQ_GET_DESCRIPTOR, | |
561 | USB_RECIP_INTERFACE | USB_TYPE_STANDARD | USB_DIR_IN, | |
562 | USB_DT_CS_GR_TRM_BLOCK << 8 | hostif->desc.bAlternateSetting, | |
563 | hostif->desc.bInterfaceNumber, data, size); | |
564 | if (err < 0) { | |
565 | kfree(data); | |
566 | return err; | |
567 | } | |
568 | ||
569 | umidi->blk_descs = data; | |
570 | umidi->blk_desc_size = size; | |
571 | return 0; | |
572 | } | |
573 | ||
574 | /* find the corresponding group terminal block descriptor */ | |
575 | static const struct usb_ms20_gr_trm_block_descriptor * | |
576 | find_group_terminal_block(struct snd_usb_midi2_interface *umidi, int id) | |
577 | { | |
578 | const unsigned char *data = umidi->blk_descs; | |
579 | int size = umidi->blk_desc_size; | |
580 | const struct usb_ms20_gr_trm_block_descriptor *desc; | |
581 | ||
582 | size -= sizeof(struct usb_ms20_gr_trm_block_header_descriptor); | |
583 | data += sizeof(struct usb_ms20_gr_trm_block_header_descriptor); | |
584 | while (size > 0 && *data && *data <= size) { | |
585 | desc = (const struct usb_ms20_gr_trm_block_descriptor *)data; | |
586 | if (desc->bLength >= sizeof(*desc) && | |
587 | desc->bDescriptorType == USB_DT_CS_GR_TRM_BLOCK && | |
588 | desc->bDescriptorSubtype == USB_MS_GR_TRM_BLOCK && | |
589 | desc->bGrpTrmBlkID == id) | |
590 | return desc; | |
591 | size -= *data; | |
592 | data += *data; | |
593 | } | |
594 | ||
595 | return NULL; | |
596 | } | |
597 | ||
598 | /* fill up the information from GTB */ | |
599 | static int parse_group_terminal_block(struct snd_usb_midi2_ump *rmidi, | |
600 | const struct usb_ms20_gr_trm_block_descriptor *desc) | |
601 | { | |
602 | struct snd_usb_audio *chip = rmidi->umidi->chip; | |
603 | struct snd_ump_endpoint *ump = rmidi->ump; | |
604 | ||
605 | usb_audio_dbg(chip, | |
606 | "GTB id %d: groups = %d / %d, type = %d\n", | |
607 | desc->bGrpTrmBlkID, desc->nGroupTrm, desc->nNumGroupTrm, | |
608 | desc->bGrpTrmBlkType); | |
609 | ||
610 | /* set default protocol */ | |
611 | switch (desc->bMIDIProtocol) { | |
612 | case USB_MS_MIDI_PROTO_1_0_64: | |
613 | case USB_MS_MIDI_PROTO_1_0_64_JRTS: | |
614 | case USB_MS_MIDI_PROTO_1_0_128: | |
615 | case USB_MS_MIDI_PROTO_1_0_128_JRTS: | |
616 | ump->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1; | |
617 | break; | |
618 | case USB_MS_MIDI_PROTO_2_0: | |
619 | case USB_MS_MIDI_PROTO_2_0_JRTS: | |
620 | ump->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI2; | |
621 | break; | |
622 | } | |
623 | ||
624 | ump->info.protocol_caps = ump->info.protocol; | |
625 | switch (desc->bMIDIProtocol) { | |
626 | case USB_MS_MIDI_PROTO_1_0_64_JRTS: | |
627 | case USB_MS_MIDI_PROTO_1_0_128_JRTS: | |
628 | case USB_MS_MIDI_PROTO_2_0_JRTS: | |
629 | ump->info.protocol_caps |= SNDRV_UMP_EP_INFO_PROTO_JRTS_TX | | |
630 | SNDRV_UMP_EP_INFO_PROTO_JRTS_RX; | |
631 | break; | |
632 | } | |
633 | ||
634 | return 0; | |
635 | } | |
636 | ||
637 | /* allocate and parse for each assigned group terminal block */ | |
638 | static int parse_group_terminal_blocks(struct snd_usb_midi2_interface *umidi) | |
639 | { | |
640 | struct snd_usb_midi2_ump *rmidi; | |
641 | const struct usb_ms20_gr_trm_block_descriptor *desc; | |
642 | int err; | |
643 | ||
644 | err = get_group_terminal_block_descs(umidi); | |
645 | if (err < 0) | |
646 | return err; | |
647 | if (!umidi->blk_descs) | |
648 | return 0; | |
649 | ||
650 | list_for_each_entry(rmidi, &umidi->rawmidi_list, list) { | |
651 | desc = find_group_terminal_block(umidi, rmidi->usb_block_id); | |
652 | if (!desc) | |
653 | continue; | |
654 | err = parse_group_terminal_block(rmidi, desc); | |
655 | if (err < 0) | |
656 | return err; | |
657 | } | |
658 | ||
659 | return 0; | |
660 | } | |
661 | ||
662 | /* parse endpoints included in the given interface and create objects */ | |
663 | static int parse_midi_2_0_endpoints(struct snd_usb_midi2_interface *umidi) | |
664 | { | |
665 | struct usb_host_interface *hostif = umidi->hostif; | |
666 | struct usb_host_endpoint *hostep; | |
667 | struct usb_ms20_endpoint_descriptor *ms_ep; | |
668 | int i, err; | |
669 | ||
670 | for (i = 0; i < hostif->desc.bNumEndpoints; i++) { | |
671 | hostep = &hostif->endpoint[i]; | |
672 | if (!usb_endpoint_xfer_bulk(&hostep->desc) && | |
673 | !usb_endpoint_xfer_int(&hostep->desc)) | |
674 | continue; | |
675 | ms_ep = find_usb_ms_endpoint_descriptor(hostep, USB_MS_GENERAL_2_0); | |
676 | if (!ms_ep) | |
677 | continue; | |
678 | if (ms_ep->bLength <= sizeof(*ms_ep)) | |
679 | continue; | |
680 | if (!ms_ep->bNumGrpTrmBlock) | |
681 | continue; | |
682 | if (ms_ep->bLength < sizeof(*ms_ep) + ms_ep->bNumGrpTrmBlock) | |
683 | continue; | |
684 | err = create_midi2_endpoint(umidi, hostep, ms_ep); | |
685 | if (err < 0) | |
686 | return err; | |
687 | } | |
688 | return 0; | |
689 | } | |
690 | ||
691 | static void free_all_midi2_umps(struct snd_usb_midi2_interface *umidi) | |
692 | { | |
693 | struct snd_usb_midi2_ump *rmidi; | |
694 | ||
695 | while (!list_empty(&umidi->rawmidi_list)) { | |
696 | rmidi = list_first_entry(&umidi->rawmidi_list, | |
697 | struct snd_usb_midi2_ump, list); | |
698 | list_del(&rmidi->list); | |
699 | kfree(rmidi); | |
700 | } | |
701 | } | |
702 | ||
703 | static int create_midi2_ump(struct snd_usb_midi2_interface *umidi, | |
704 | struct snd_usb_midi2_endpoint *ep_in, | |
705 | struct snd_usb_midi2_endpoint *ep_out, | |
706 | int blk_id) | |
707 | { | |
708 | struct snd_usb_midi2_ump *rmidi; | |
709 | struct snd_ump_endpoint *ump; | |
710 | int input, output; | |
711 | char idstr[16]; | |
712 | int err; | |
713 | ||
714 | rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL); | |
715 | if (!rmidi) | |
716 | return -ENOMEM; | |
717 | INIT_LIST_HEAD(&rmidi->list); | |
718 | rmidi->dev = umidi->chip->dev; | |
719 | rmidi->umidi = umidi; | |
720 | rmidi->usb_block_id = blk_id; | |
721 | ||
722 | rmidi->index = umidi->chip->num_rawmidis; | |
723 | snprintf(idstr, sizeof(idstr), "UMP %d", rmidi->index); | |
724 | input = ep_in ? 1 : 0; | |
725 | output = ep_out ? 1 : 0; | |
726 | err = snd_ump_endpoint_new(umidi->chip->card, idstr, rmidi->index, | |
727 | output, input, &ump); | |
728 | if (err < 0) { | |
729 | usb_audio_dbg(umidi->chip, "Failed to create a UMP object\n"); | |
730 | kfree(rmidi); | |
731 | return err; | |
732 | } | |
733 | ||
734 | rmidi->ump = ump; | |
735 | umidi->chip->num_rawmidis++; | |
736 | ||
737 | ump->private_data = rmidi; | |
738 | ||
739 | if (input) | |
740 | snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_INPUT, | |
741 | &input_ops); | |
742 | if (output) | |
743 | snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_OUTPUT, | |
744 | &output_ops); | |
745 | ||
746 | rmidi->eps[STR_IN] = ep_in; | |
747 | rmidi->eps[STR_OUT] = ep_out; | |
748 | if (ep_in) { | |
749 | ep_in->pair = ep_out; | |
750 | ep_in->rmidi = rmidi; | |
751 | } | |
752 | if (ep_out) { | |
753 | ep_out->pair = ep_in; | |
754 | ep_out->rmidi = rmidi; | |
755 | } | |
756 | ||
757 | list_add_tail(&rmidi->list, &umidi->rawmidi_list); | |
758 | return 0; | |
759 | } | |
760 | ||
761 | /* find the UMP EP with the given USB block id */ | |
762 | static struct snd_usb_midi2_ump * | |
763 | find_midi2_ump(struct snd_usb_midi2_interface *umidi, int blk_id) | |
764 | { | |
765 | struct snd_usb_midi2_ump *rmidi; | |
766 | ||
767 | list_for_each_entry(rmidi, &umidi->rawmidi_list, list) { | |
768 | if (rmidi->usb_block_id == blk_id) | |
769 | return rmidi; | |
770 | } | |
771 | return NULL; | |
772 | } | |
773 | ||
774 | /* look for the matching output endpoint and create UMP object if found */ | |
775 | static int find_matching_ep_partner(struct snd_usb_midi2_interface *umidi, | |
776 | struct snd_usb_midi2_endpoint *ep, | |
777 | int blk_id) | |
778 | { | |
779 | struct snd_usb_midi2_endpoint *pair_ep; | |
780 | int blk; | |
781 | ||
782 | usb_audio_dbg(umidi->chip, "Looking for a pair for EP-in 0x%02x\n", | |
783 | ep->endpoint); | |
784 | list_for_each_entry(pair_ep, &umidi->ep_list, list) { | |
785 | if (pair_ep->direction != STR_OUT) | |
786 | continue; | |
787 | if (pair_ep->pair) | |
788 | continue; /* already paired */ | |
789 | for (blk = 0; blk < pair_ep->ms_ep->bNumGrpTrmBlock; blk++) { | |
790 | if (pair_ep->ms_ep->baAssoGrpTrmBlkID[blk] == blk_id) { | |
791 | usb_audio_dbg(umidi->chip, | |
792 | "Found a match with EP-out 0x%02x blk %d\n", | |
793 | pair_ep->endpoint, blk); | |
794 | return create_midi2_ump(umidi, ep, pair_ep, blk_id); | |
795 | } | |
796 | } | |
797 | } | |
798 | return 0; | |
799 | } | |
800 | ||
801 | static void snd_usb_midi_v2_free(struct snd_usb_midi2_interface *umidi) | |
802 | { | |
803 | free_all_midi2_endpoints(umidi); | |
804 | free_all_midi2_umps(umidi); | |
805 | list_del(&umidi->list); | |
806 | kfree(umidi->blk_descs); | |
807 | kfree(umidi); | |
808 | } | |
809 | ||
810 | /* parse the interface for MIDI 2.0 */ | |
811 | static int parse_midi_2_0(struct snd_usb_midi2_interface *umidi) | |
812 | { | |
813 | struct snd_usb_midi2_endpoint *ep; | |
814 | int blk, id, err; | |
815 | ||
816 | /* First, create an object for each USB MIDI Endpoint */ | |
817 | err = parse_midi_2_0_endpoints(umidi); | |
818 | if (err < 0) | |
819 | return err; | |
820 | if (list_empty(&umidi->ep_list)) { | |
821 | usb_audio_warn(umidi->chip, "No MIDI endpoints found\n"); | |
822 | return -ENODEV; | |
823 | } | |
824 | ||
825 | /* | |
826 | * Next, look for EP I/O pairs that are found in group terminal blocks | |
827 | * A UMP object is created for each EP I/O pair as bidirecitonal | |
828 | * UMP EP | |
829 | */ | |
830 | list_for_each_entry(ep, &umidi->ep_list, list) { | |
831 | /* only input in this loop; output is matched in find_midi_ump() */ | |
832 | if (ep->direction != STR_IN) | |
833 | continue; | |
834 | for (blk = 0; blk < ep->ms_ep->bNumGrpTrmBlock; blk++) { | |
835 | id = ep->ms_ep->baAssoGrpTrmBlkID[blk]; | |
836 | err = find_matching_ep_partner(umidi, ep, id); | |
837 | if (err < 0) | |
838 | return err; | |
839 | } | |
840 | } | |
841 | ||
842 | /* | |
843 | * For the remaining EPs, treat as singles, create a UMP object with | |
844 | * unidirectional EP | |
845 | */ | |
846 | list_for_each_entry(ep, &umidi->ep_list, list) { | |
847 | if (ep->rmidi) | |
848 | continue; /* already paired */ | |
849 | for (blk = 0; blk < ep->ms_ep->bNumGrpTrmBlock; blk++) { | |
850 | id = ep->ms_ep->baAssoGrpTrmBlkID[blk]; | |
851 | if (find_midi2_ump(umidi, id)) | |
852 | continue; | |
853 | usb_audio_dbg(umidi->chip, | |
854 | "Creating a unidirection UMP for EP=0x%02x, blk=%d\n", | |
855 | ep->endpoint, id); | |
856 | if (ep->direction == STR_IN) | |
857 | err = create_midi2_ump(umidi, ep, NULL, id); | |
858 | else | |
859 | err = create_midi2_ump(umidi, NULL, ep, id); | |
860 | if (err < 0) | |
861 | return err; | |
862 | break; | |
863 | } | |
864 | } | |
865 | ||
866 | return 0; | |
867 | } | |
868 | ||
869 | /* is the given interface for MIDI 2.0? */ | |
870 | static bool is_midi2_altset(struct usb_host_interface *hostif) | |
871 | { | |
872 | struct usb_ms_header_descriptor *ms_header = | |
873 | (struct usb_ms_header_descriptor *)hostif->extra; | |
874 | ||
875 | if (hostif->extralen < 7 || | |
876 | ms_header->bLength < 7 || | |
877 | ms_header->bDescriptorType != USB_DT_CS_INTERFACE || | |
878 | ms_header->bDescriptorSubtype != UAC_HEADER) | |
879 | return false; | |
880 | ||
881 | return le16_to_cpu(ms_header->bcdMSC) == USB_MS_REV_MIDI_2_0; | |
882 | } | |
883 | ||
884 | /* change the altsetting */ | |
885 | static int set_altset(struct snd_usb_midi2_interface *umidi) | |
886 | { | |
887 | usb_audio_dbg(umidi->chip, "Setting host iface %d:%d\n", | |
888 | umidi->hostif->desc.bInterfaceNumber, | |
889 | umidi->hostif->desc.bAlternateSetting); | |
890 | return usb_set_interface(umidi->chip->dev, | |
891 | umidi->hostif->desc.bInterfaceNumber, | |
892 | umidi->hostif->desc.bAlternateSetting); | |
893 | } | |
894 | ||
06cf3bf0 TI |
895 | /* fill UMP Endpoint name string from USB descriptor */ |
896 | static void fill_ump_ep_name(struct snd_ump_endpoint *ump, | |
897 | struct usb_device *dev, int id) | |
898 | { | |
899 | usb_string(dev, id, ump->info.name, sizeof(ump->info.name)); | |
900 | } | |
901 | ||
ff49d1df TI |
902 | /* fill the fallback name string for each rawmidi instance */ |
903 | static void set_fallback_rawmidi_names(struct snd_usb_midi2_interface *umidi) | |
904 | { | |
06cf3bf0 | 905 | struct usb_device *dev = umidi->chip->dev; |
ff49d1df | 906 | struct snd_usb_midi2_ump *rmidi; |
06cf3bf0 | 907 | struct snd_ump_endpoint *ump; |
ff49d1df TI |
908 | |
909 | list_for_each_entry(rmidi, &umidi->rawmidi_list, list) { | |
06cf3bf0 TI |
910 | ump = rmidi->ump; |
911 | /* fill UMP EP name from USB descriptors */ | |
912 | if (!*ump->info.name && umidi->hostif->desc.iInterface) | |
913 | fill_ump_ep_name(ump, dev, umidi->hostif->desc.iInterface); | |
914 | else if (!*ump->info.name && dev->descriptor.iProduct) | |
915 | fill_ump_ep_name(ump, dev, dev->descriptor.iProduct); | |
916 | /* fill fallback name */ | |
917 | if (!*ump->info.name) | |
918 | sprintf(ump->info.name, "USB MIDI %d", rmidi->index); | |
919 | /* copy as rawmidi name if not set */ | |
920 | if (!*ump->core.name) | |
921 | strscpy(ump->core.name, ump->info.name, | |
922 | sizeof(ump->core.name)); | |
923 | /* use serial number string as unique UMP product id */ | |
924 | if (!*ump->info.product_id && dev->descriptor.iSerialNumber) | |
925 | usb_string(dev, dev->descriptor.iSerialNumber, | |
926 | ump->info.product_id, | |
927 | sizeof(ump->info.product_id)); | |
ff49d1df TI |
928 | } |
929 | } | |
930 | ||
931 | /* create MIDI interface; fallback to MIDI 1.0 if needed */ | |
932 | int snd_usb_midi_v2_create(struct snd_usb_audio *chip, | |
933 | struct usb_interface *iface, | |
934 | const struct snd_usb_audio_quirk *quirk, | |
935 | unsigned int usb_id) | |
936 | { | |
937 | struct snd_usb_midi2_interface *umidi; | |
938 | struct usb_host_interface *hostif; | |
939 | int err; | |
940 | ||
941 | usb_audio_dbg(chip, "Parsing interface %d...\n", | |
942 | iface->altsetting[0].desc.bInterfaceNumber); | |
943 | ||
944 | /* fallback to MIDI 1.0? */ | |
945 | if (!midi2_enable) { | |
946 | usb_audio_info(chip, "Falling back to MIDI 1.0 by module option\n"); | |
947 | goto fallback_to_midi1; | |
948 | } | |
949 | if ((quirk && quirk->type != QUIRK_MIDI_STANDARD_INTERFACE) || | |
950 | iface->num_altsetting < 2) { | |
951 | usb_audio_info(chip, "Quirk or no altest; falling back to MIDI 1.0\n"); | |
952 | goto fallback_to_midi1; | |
953 | } | |
954 | hostif = &iface->altsetting[1]; | |
955 | if (!is_midi2_altset(hostif)) { | |
956 | usb_audio_info(chip, "No MIDI 2.0 at altset 1, falling back to MIDI 1.0\n"); | |
957 | goto fallback_to_midi1; | |
958 | } | |
959 | if (!hostif->desc.bNumEndpoints) { | |
960 | usb_audio_info(chip, "No endpoint at altset 1, falling back to MIDI 1.0\n"); | |
961 | goto fallback_to_midi1; | |
962 | } | |
963 | ||
964 | usb_audio_dbg(chip, "Creating a MIDI 2.0 instance for %d:%d\n", | |
965 | hostif->desc.bInterfaceNumber, | |
966 | hostif->desc.bAlternateSetting); | |
967 | ||
968 | umidi = kzalloc(sizeof(*umidi), GFP_KERNEL); | |
969 | if (!umidi) | |
970 | return -ENOMEM; | |
971 | umidi->chip = chip; | |
972 | umidi->iface = iface; | |
973 | umidi->hostif = hostif; | |
974 | INIT_LIST_HEAD(&umidi->rawmidi_list); | |
975 | INIT_LIST_HEAD(&umidi->ep_list); | |
976 | ||
977 | list_add_tail(&umidi->list, &chip->midi_v2_list); | |
978 | ||
979 | err = set_altset(umidi); | |
980 | if (err < 0) { | |
981 | usb_audio_err(chip, "Failed to set altset\n"); | |
982 | goto error; | |
983 | } | |
984 | ||
985 | /* assume only altset 1 corresponding to MIDI 2.0 interface */ | |
986 | err = parse_midi_2_0(umidi); | |
987 | if (err < 0) { | |
988 | usb_audio_err(chip, "Failed to parse MIDI 2.0 interface\n"); | |
989 | goto error; | |
990 | } | |
991 | ||
992 | /* parse USB group terminal blocks */ | |
993 | err = parse_group_terminal_blocks(umidi); | |
994 | if (err < 0) { | |
995 | usb_audio_err(chip, "Failed to parse GTB\n"); | |
996 | goto error; | |
997 | } | |
998 | ||
999 | err = start_input_streams(umidi); | |
1000 | if (err < 0) { | |
1001 | usb_audio_err(chip, "Failed to start input streams\n"); | |
1002 | goto error; | |
1003 | } | |
1004 | ||
1005 | set_fallback_rawmidi_names(umidi); | |
1006 | return 0; | |
1007 | ||
1008 | error: | |
1009 | snd_usb_midi_v2_free(umidi); | |
1010 | return err; | |
1011 | ||
1012 | fallback_to_midi1: | |
1013 | return __snd_usbmidi_create(chip->card, iface, &chip->midi_list, | |
1014 | quirk, usb_id, &chip->num_rawmidis); | |
1015 | } | |
1016 | ||
1017 | static void suspend_midi2_endpoint(struct snd_usb_midi2_endpoint *ep) | |
1018 | { | |
1019 | kill_midi_urbs(ep, true); | |
1020 | drain_urb_queue(ep); | |
1021 | } | |
1022 | ||
1023 | void snd_usb_midi_v2_suspend_all(struct snd_usb_audio *chip) | |
1024 | { | |
1025 | struct snd_usb_midi2_interface *umidi; | |
1026 | struct snd_usb_midi2_endpoint *ep; | |
1027 | ||
1028 | list_for_each_entry(umidi, &chip->midi_v2_list, list) { | |
1029 | list_for_each_entry(ep, &umidi->ep_list, list) | |
1030 | suspend_midi2_endpoint(ep); | |
1031 | } | |
1032 | } | |
1033 | ||
1034 | static void resume_midi2_endpoint(struct snd_usb_midi2_endpoint *ep) | |
1035 | { | |
1036 | ep->running = ep->suspended; | |
1037 | if (ep->direction == STR_IN) | |
1038 | submit_io_urbs(ep); | |
1039 | /* FIXME: does it all? */ | |
1040 | } | |
1041 | ||
1042 | void snd_usb_midi_v2_resume_all(struct snd_usb_audio *chip) | |
1043 | { | |
1044 | struct snd_usb_midi2_interface *umidi; | |
1045 | struct snd_usb_midi2_endpoint *ep; | |
1046 | ||
1047 | list_for_each_entry(umidi, &chip->midi_v2_list, list) { | |
1048 | set_altset(umidi); | |
1049 | list_for_each_entry(ep, &umidi->ep_list, list) | |
1050 | resume_midi2_endpoint(ep); | |
1051 | } | |
1052 | } | |
1053 | ||
1054 | void snd_usb_midi_v2_disconnect_all(struct snd_usb_audio *chip) | |
1055 | { | |
1056 | struct snd_usb_midi2_interface *umidi; | |
1057 | struct snd_usb_midi2_endpoint *ep; | |
1058 | ||
1059 | list_for_each_entry(umidi, &chip->midi_v2_list, list) { | |
1060 | umidi->disconnected = 1; | |
1061 | list_for_each_entry(ep, &umidi->ep_list, list) { | |
1062 | ep->disconnected = 1; | |
1063 | kill_midi_urbs(ep, false); | |
1064 | drain_urb_queue(ep); | |
1065 | } | |
1066 | } | |
1067 | } | |
1068 | ||
1069 | /* release the MIDI instance */ | |
1070 | void snd_usb_midi_v2_free_all(struct snd_usb_audio *chip) | |
1071 | { | |
1072 | struct snd_usb_midi2_interface *umidi, *next; | |
1073 | ||
1074 | list_for_each_entry_safe(umidi, next, &chip->midi_v2_list, list) | |
1075 | snd_usb_midi_v2_free(umidi); | |
1076 | } |