xref: /openbmc/linux/sound/core/ump.c (revision 9b446941)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Universal MIDI Packet (UMP) support
4  */
5 
6 #include <linux/list.h>
7 #include <linux/slab.h>
8 #include <linux/module.h>
9 #include <linux/export.h>
10 #include <linux/mm.h>
11 #include <sound/core.h>
12 #include <sound/rawmidi.h>
13 #include <sound/ump.h>
14 #include "ump_convert.h"
15 
16 #define ump_err(ump, fmt, args...)	dev_err(&(ump)->core.dev, fmt, ##args)
17 #define ump_warn(ump, fmt, args...)	dev_warn(&(ump)->core.dev, fmt, ##args)
18 #define ump_info(ump, fmt, args...)	dev_info(&(ump)->core.dev, fmt, ##args)
19 #define ump_dbg(ump, fmt, args...)	dev_dbg(&(ump)->core.dev, fmt, ##args)
20 
21 static int snd_ump_dev_register(struct snd_rawmidi *rmidi);
22 static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi);
23 static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd,
24 			  void __user *argp);
25 static void snd_ump_proc_read(struct snd_info_entry *entry,
26 			      struct snd_info_buffer *buffer);
27 static int snd_ump_rawmidi_open(struct snd_rawmidi_substream *substream);
28 static int snd_ump_rawmidi_close(struct snd_rawmidi_substream *substream);
29 static void snd_ump_rawmidi_trigger(struct snd_rawmidi_substream *substream,
30 				    int up);
31 static void snd_ump_rawmidi_drain(struct snd_rawmidi_substream *substream);
32 
33 #if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
34 static int process_legacy_output(struct snd_ump_endpoint *ump,
35 				 u32 *buffer, int count);
36 static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src,
37 				 int words);
38 #else
39 static inline int process_legacy_output(struct snd_ump_endpoint *ump,
40 					u32 *buffer, int count)
41 {
42 	return 0;
43 }
44 static inline void process_legacy_input(struct snd_ump_endpoint *ump,
45 					const u32 *src, int words)
46 {
47 }
48 #endif
49 
50 static const struct snd_rawmidi_global_ops snd_ump_rawmidi_ops = {
51 	.dev_register = snd_ump_dev_register,
52 	.dev_unregister = snd_ump_dev_unregister,
53 	.ioctl = snd_ump_ioctl,
54 	.proc_read = snd_ump_proc_read,
55 };
56 
57 static const struct snd_rawmidi_ops snd_ump_rawmidi_input_ops = {
58 	.open = snd_ump_rawmidi_open,
59 	.close = snd_ump_rawmidi_close,
60 	.trigger = snd_ump_rawmidi_trigger,
61 };
62 
63 static const struct snd_rawmidi_ops snd_ump_rawmidi_output_ops = {
64 	.open = snd_ump_rawmidi_open,
65 	.close = snd_ump_rawmidi_close,
66 	.trigger = snd_ump_rawmidi_trigger,
67 	.drain = snd_ump_rawmidi_drain,
68 };
69 
70 static void snd_ump_endpoint_free(struct snd_rawmidi *rmidi)
71 {
72 	struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
73 	struct snd_ump_block *fb;
74 
75 	while (!list_empty(&ump->block_list)) {
76 		fb = list_first_entry(&ump->block_list, struct snd_ump_block,
77 				      list);
78 		list_del(&fb->list);
79 		if (fb->private_free)
80 			fb->private_free(fb);
81 		kfree(fb);
82 	}
83 
84 	if (ump->private_free)
85 		ump->private_free(ump);
86 
87 #if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
88 	snd_ump_convert_free(ump);
89 #endif
90 }
91 
92 /**
93  * snd_ump_endpoint_new - create a UMP Endpoint object
94  * @card: the card instance
95  * @id: the id string for rawmidi
96  * @device: the device index for rawmidi
97  * @output: 1 for enabling output
98  * @input: 1 for enabling input
99  * @ump_ret: the pointer to store the new UMP instance
100  *
101  * Creates a new UMP Endpoint object. A UMP Endpoint is tied with one rawmidi
102  * instance with one input and/or one output rawmidi stream (either uni-
103  * or bi-directional). A UMP Endpoint may contain one or multiple UMP Blocks
104  * that consist of one or multiple UMP Groups.
105  *
106  * Use snd_rawmidi_set_ops() to set the operators to the new instance.
107  * Unlike snd_rawmidi_new(), this function sets up the info_flags by itself
108  * depending on the given @output and @input.
109  *
110  * The device has SNDRV_RAWMIDI_INFO_UMP flag set and a different device
111  * file ("umpCxDx") than a standard MIDI 1.x device ("midiCxDx") is
112  * created.
113  *
114  * Return: Zero if successful, or a negative error code on failure.
115  */
116 int snd_ump_endpoint_new(struct snd_card *card, char *id, int device,
117 			 int output, int input,
118 			 struct snd_ump_endpoint **ump_ret)
119 {
120 	unsigned int info_flags = SNDRV_RAWMIDI_INFO_UMP;
121 	struct snd_ump_endpoint *ump;
122 	int err;
123 
124 	if (input)
125 		info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
126 	if (output)
127 		info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
128 	if (input && output)
129 		info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
130 
131 	ump = kzalloc(sizeof(*ump), GFP_KERNEL);
132 	if (!ump)
133 		return -ENOMEM;
134 	INIT_LIST_HEAD(&ump->block_list);
135 	mutex_init(&ump->open_mutex);
136 #if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
137 	spin_lock_init(&ump->legacy_locks[0]);
138 	spin_lock_init(&ump->legacy_locks[1]);
139 #endif
140 	err = snd_rawmidi_init(&ump->core, card, id, device,
141 			       output, input, info_flags);
142 	if (err < 0) {
143 		snd_rawmidi_free(&ump->core);
144 		return err;
145 	}
146 
147 	ump->info.card = card->number;
148 	ump->info.device = device;
149 
150 	ump->core.private_free = snd_ump_endpoint_free;
151 	ump->core.ops = &snd_ump_rawmidi_ops;
152 	if (input)
153 		snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_INPUT,
154 				    &snd_ump_rawmidi_input_ops);
155 	if (output)
156 		snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_OUTPUT,
157 				    &snd_ump_rawmidi_output_ops);
158 
159 	ump_dbg(ump, "Created a UMP EP #%d (%s)\n", device, id);
160 	*ump_ret = ump;
161 	return 0;
162 }
163 EXPORT_SYMBOL_GPL(snd_ump_endpoint_new);
164 
165 /*
166  * Device register / unregister hooks;
167  *  do nothing, placeholders for avoiding the default rawmidi handling
168  */
169 
170 #if IS_ENABLED(CONFIG_SND_SEQUENCER)
171 static void snd_ump_dev_seq_free(struct snd_seq_device *device)
172 {
173 	struct snd_ump_endpoint *ump = device->private_data;
174 
175 	ump->seq_dev = NULL;
176 }
177 #endif
178 
179 static int snd_ump_dev_register(struct snd_rawmidi *rmidi)
180 {
181 #if IS_ENABLED(CONFIG_SND_SEQUENCER)
182 	struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
183 	int err;
184 
185 	err = snd_seq_device_new(ump->core.card, ump->core.device,
186 				 SNDRV_SEQ_DEV_ID_UMP, 0, &ump->seq_dev);
187 	if (err < 0)
188 		return err;
189 	ump->seq_dev->private_data = ump;
190 	ump->seq_dev->private_free = snd_ump_dev_seq_free;
191 	snd_device_register(ump->core.card, ump->seq_dev);
192 #endif
193 	return 0;
194 }
195 
196 static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi)
197 {
198 	return 0;
199 }
200 
201 static struct snd_ump_block *
202 snd_ump_get_block(struct snd_ump_endpoint *ump, unsigned char id)
203 {
204 	struct snd_ump_block *fb;
205 
206 	list_for_each_entry(fb, &ump->block_list, list) {
207 		if (fb->info.block_id == id)
208 			return fb;
209 	}
210 	return NULL;
211 }
212 
213 /*
214  * rawmidi ops for UMP endpoint
215  */
216 static int snd_ump_rawmidi_open(struct snd_rawmidi_substream *substream)
217 {
218 	struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
219 	int dir = substream->stream;
220 	int err;
221 
222 	if (ump->substreams[dir])
223 		return -EBUSY;
224 	err = ump->ops->open(ump, dir);
225 	if (err < 0)
226 		return err;
227 	ump->substreams[dir] = substream;
228 	return 0;
229 }
230 
231 static int snd_ump_rawmidi_close(struct snd_rawmidi_substream *substream)
232 {
233 	struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
234 	int dir = substream->stream;
235 
236 	ump->substreams[dir] = NULL;
237 	ump->ops->close(ump, dir);
238 	return 0;
239 }
240 
241 static void snd_ump_rawmidi_trigger(struct snd_rawmidi_substream *substream,
242 				    int up)
243 {
244 	struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
245 	int dir = substream->stream;
246 
247 	ump->ops->trigger(ump, dir, up);
248 }
249 
250 static void snd_ump_rawmidi_drain(struct snd_rawmidi_substream *substream)
251 {
252 	struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
253 
254 	if (ump->ops->drain)
255 		ump->ops->drain(ump, SNDRV_RAWMIDI_STREAM_OUTPUT);
256 }
257 
258 /* number of 32bit words per message type */
259 static unsigned char ump_packet_words[0x10] = {
260 	1, 1, 1, 2, 2, 4, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4
261 };
262 
263 /* parse the UMP packet data;
264  * the data is copied onto ump->input_buf[].
265  * When a full packet is completed, returns the number of words (from 1 to 4).
266  * OTOH, if the packet is incomplete, returns 0.
267  */
268 static int snd_ump_receive_ump_val(struct snd_ump_endpoint *ump, u32 val)
269 {
270 	int words;
271 
272 	if (!ump->input_pending)
273 		ump->input_pending = ump_packet_words[ump_message_type(val)];
274 
275 	ump->input_buf[ump->input_buf_head++] = val;
276 	ump->input_pending--;
277 	if (!ump->input_pending) {
278 		words = ump->input_buf_head;
279 		ump->input_buf_head = 0;
280 		return words;
281 	}
282 	return 0;
283 }
284 
285 /**
286  * snd_ump_receive - transfer UMP packets from the device
287  * @ump: the UMP endpoint
288  * @buffer: the buffer pointer to transfer
289  * @count: byte size to transfer
290  *
291  * Called from the driver to submit the received UMP packets from the device
292  * to user-space.  It's essentially a wrapper of rawmidi_receive().
293  * The data to receive is in CPU-native endianness.
294  */
295 int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count)
296 {
297 	struct snd_rawmidi_substream *substream;
298 	const u32 *p = buffer;
299 	int n, words = count >> 2;
300 
301 	while (words--) {
302 		n = snd_ump_receive_ump_val(ump, *p++);
303 		if (!n)
304 			continue;
305 #if IS_ENABLED(CONFIG_SND_SEQUENCER)
306 		if (ump->seq_ops)
307 			ump->seq_ops->input_receive(ump, ump->input_buf, n);
308 #endif
309 		process_legacy_input(ump, ump->input_buf, n);
310 	}
311 
312 	substream = ump->substreams[SNDRV_RAWMIDI_STREAM_INPUT];
313 	if (!substream)
314 		return 0;
315 	return snd_rawmidi_receive(substream, (const char *)buffer, count);
316 }
317 EXPORT_SYMBOL_GPL(snd_ump_receive);
318 
319 /**
320  * snd_ump_transmit - transmit UMP packets
321  * @ump: the UMP endpoint
322  * @buffer: the buffer pointer to transfer
323  * @count: byte size to transfer
324  *
325  * Called from the driver to obtain the UMP packets from user-space to the
326  * device.  It's essentially a wrapper of rawmidi_transmit().
327  * The data to transmit is in CPU-native endianness.
328  */
329 int snd_ump_transmit(struct snd_ump_endpoint *ump, u32 *buffer, int count)
330 {
331 	struct snd_rawmidi_substream *substream =
332 		ump->substreams[SNDRV_RAWMIDI_STREAM_OUTPUT];
333 	int err;
334 
335 	if (!substream)
336 		return -ENODEV;
337 	err = snd_rawmidi_transmit(substream, (char *)buffer, count);
338 	/* received either data or an error? */
339 	if (err)
340 		return err;
341 	return process_legacy_output(ump, buffer, count);
342 }
343 EXPORT_SYMBOL_GPL(snd_ump_transmit);
344 
345 /**
346  * snd_ump_block_new - Create a UMP block
347  * @ump: UMP object
348  * @blk: block ID number to create
349  * @direction: direction (in/out/bidirection)
350  * @first_group: the first group ID (0-based)
351  * @num_groups: the number of groups in this block
352  * @blk_ret: the pointer to store the resultant block object
353  */
354 int snd_ump_block_new(struct snd_ump_endpoint *ump, unsigned int blk,
355 		      unsigned int direction, unsigned int first_group,
356 		      unsigned int num_groups, struct snd_ump_block **blk_ret)
357 {
358 	struct snd_ump_block *fb, *p;
359 
360 	if (blk < 0 || blk >= SNDRV_UMP_MAX_BLOCKS)
361 		return -EINVAL;
362 
363 	if (snd_ump_get_block(ump, blk))
364 		return -EBUSY;
365 
366 	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
367 	if (!fb)
368 		return -ENOMEM;
369 
370 	fb->ump = ump;
371 	fb->info.card = ump->info.card;
372 	fb->info.device = ump->info.device;
373 	fb->info.block_id = blk;
374 	if (blk >= ump->info.num_blocks)
375 		ump->info.num_blocks = blk + 1;
376 	fb->info.direction = direction;
377 	fb->info.active = 1;
378 	fb->info.first_group = first_group;
379 	fb->info.num_groups = num_groups;
380 	/* fill the default name, may be overwritten to a better name */
381 	snprintf(fb->info.name, sizeof(fb->info.name), "Group %d-%d",
382 		 first_group + 1, first_group + num_groups);
383 
384 	/* put the entry in the ordered list */
385 	list_for_each_entry(p, &ump->block_list, list) {
386 		if (p->info.block_id > blk) {
387 			list_add_tail(&fb->list, &p->list);
388 			goto added;
389 		}
390 	}
391 	list_add_tail(&fb->list, &ump->block_list);
392 
393  added:
394 	ump_dbg(ump, "Created a UMP Block #%d (%s)\n", blk, fb->info.name);
395 	*blk_ret = fb;
396 	return 0;
397 }
398 EXPORT_SYMBOL_GPL(snd_ump_block_new);
399 
400 static int snd_ump_ioctl_block(struct snd_ump_endpoint *ump,
401 			       struct snd_ump_block_info __user *argp)
402 {
403 	struct snd_ump_block *fb;
404 	unsigned char id;
405 
406 	if (get_user(id, &argp->block_id))
407 		return -EFAULT;
408 	fb = snd_ump_get_block(ump, id);
409 	if (!fb)
410 		return -ENOENT;
411 	if (copy_to_user(argp, &fb->info, sizeof(fb->info)))
412 		return -EFAULT;
413 	return 0;
414 }
415 
416 /*
417  * Handle UMP-specific ioctls; called from snd_rawmidi_ioctl()
418  */
419 static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd,
420 			  void __user *argp)
421 {
422 	struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
423 
424 	switch (cmd) {
425 	case SNDRV_UMP_IOCTL_ENDPOINT_INFO:
426 		if (copy_to_user(argp, &ump->info, sizeof(ump->info)))
427 			return -EFAULT;
428 		return 0;
429 	case SNDRV_UMP_IOCTL_BLOCK_INFO:
430 		return snd_ump_ioctl_block(ump, argp);
431 	default:
432 		ump_dbg(ump, "rawmidi: unknown command = 0x%x\n", cmd);
433 		return -ENOTTY;
434 	}
435 }
436 
437 static const char *ump_direction_string(int dir)
438 {
439 	switch (dir) {
440 	case SNDRV_UMP_DIR_INPUT:
441 		return "input";
442 	case SNDRV_UMP_DIR_OUTPUT:
443 		return "output";
444 	case SNDRV_UMP_DIR_BIDIRECTION:
445 		return "bidirection";
446 	default:
447 		return "unknown";
448 	}
449 }
450 
451 /* Additional proc file output */
452 static void snd_ump_proc_read(struct snd_info_entry *entry,
453 			      struct snd_info_buffer *buffer)
454 {
455 	struct snd_rawmidi *rmidi = entry->private_data;
456 	struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
457 	struct snd_ump_block *fb;
458 
459 	snd_iprintf(buffer, "EP Name: %s\n", ump->info.name);
460 	snd_iprintf(buffer, "EP Product ID: %s\n", ump->info.product_id);
461 	snd_iprintf(buffer, "UMP Version: 0x%04x\n", ump->info.version);
462 	snd_iprintf(buffer, "Protocol Caps: 0x%08x\n", ump->info.protocol_caps);
463 	snd_iprintf(buffer, "Protocol: 0x%08x\n", ump->info.protocol);
464 	snd_iprintf(buffer, "Num Blocks: %d\n\n", ump->info.num_blocks);
465 
466 	list_for_each_entry(fb, &ump->block_list, list) {
467 		snd_iprintf(buffer, "Block %d (%s)\n", fb->info.block_id,
468 			    fb->info.name);
469 		snd_iprintf(buffer, "  Direction: %s\n",
470 			    ump_direction_string(fb->info.direction));
471 		snd_iprintf(buffer, "  Active: %s\n",
472 			    fb->info.active ? "Yes" : "No");
473 		snd_iprintf(buffer, "  Groups: %d-%d\n",
474 			    fb->info.first_group + 1,
475 			    fb->info.first_group + fb->info.num_groups);
476 		snd_iprintf(buffer, "  Is MIDI1: %s%s\n",
477 			    (fb->info.flags & SNDRV_UMP_BLOCK_IS_MIDI1) ? "Yes" : "No",
478 			    (fb->info.flags & SNDRV_UMP_BLOCK_IS_LOWSPEED) ? " (Low Speed)" : "");
479 		snd_iprintf(buffer, "\n");
480 	}
481 }
482 
483 #if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
484 /*
485  * Legacy rawmidi support
486  */
487 static int snd_ump_legacy_open(struct snd_rawmidi_substream *substream)
488 {
489 	struct snd_ump_endpoint *ump = substream->rmidi->private_data;
490 	int dir = substream->stream;
491 	int group = substream->number;
492 	int err;
493 
494 	mutex_lock(&ump->open_mutex);
495 	if (ump->legacy_substreams[dir][group]) {
496 		err = -EBUSY;
497 		goto unlock;
498 	}
499 	if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
500 		if (!ump->legacy_out_opens) {
501 			err = snd_rawmidi_kernel_open(&ump->core, 0,
502 						      SNDRV_RAWMIDI_LFLG_OUTPUT |
503 						      SNDRV_RAWMIDI_LFLG_APPEND,
504 						      &ump->legacy_out_rfile);
505 			if (err < 0)
506 				goto unlock;
507 		}
508 		ump->legacy_out_opens++;
509 		snd_ump_reset_convert_to_ump(ump, group);
510 	}
511 	spin_lock_irq(&ump->legacy_locks[dir]);
512 	ump->legacy_substreams[dir][group] = substream;
513 	spin_unlock_irq(&ump->legacy_locks[dir]);
514  unlock:
515 	mutex_unlock(&ump->open_mutex);
516 	return 0;
517 }
518 
519 static int snd_ump_legacy_close(struct snd_rawmidi_substream *substream)
520 {
521 	struct snd_ump_endpoint *ump = substream->rmidi->private_data;
522 	int dir = substream->stream;
523 	int group = substream->number;
524 
525 	mutex_lock(&ump->open_mutex);
526 	spin_lock_irq(&ump->legacy_locks[dir]);
527 	ump->legacy_substreams[dir][group] = NULL;
528 	spin_unlock_irq(&ump->legacy_locks[dir]);
529 	if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
530 		if (!--ump->legacy_out_opens)
531 			snd_rawmidi_kernel_release(&ump->legacy_out_rfile);
532 	}
533 	mutex_unlock(&ump->open_mutex);
534 	return 0;
535 }
536 
537 static void snd_ump_legacy_trigger(struct snd_rawmidi_substream *substream,
538 				   int up)
539 {
540 	struct snd_ump_endpoint *ump = substream->rmidi->private_data;
541 	int dir = substream->stream;
542 
543 	ump->ops->trigger(ump, dir, up);
544 }
545 
546 static void snd_ump_legacy_drain(struct snd_rawmidi_substream *substream)
547 {
548 	struct snd_ump_endpoint *ump = substream->rmidi->private_data;
549 
550 	if (ump->ops->drain)
551 		ump->ops->drain(ump, SNDRV_RAWMIDI_STREAM_OUTPUT);
552 }
553 
554 static int snd_ump_legacy_dev_register(struct snd_rawmidi *rmidi)
555 {
556 	/* dummy, just for avoiding create superfluous seq clients */
557 	return 0;
558 }
559 
560 static const struct snd_rawmidi_ops snd_ump_legacy_input_ops = {
561 	.open = snd_ump_legacy_open,
562 	.close = snd_ump_legacy_close,
563 	.trigger = snd_ump_legacy_trigger,
564 };
565 
566 static const struct snd_rawmidi_ops snd_ump_legacy_output_ops = {
567 	.open = snd_ump_legacy_open,
568 	.close = snd_ump_legacy_close,
569 	.trigger = snd_ump_legacy_trigger,
570 	.drain = snd_ump_legacy_drain,
571 };
572 
573 static const struct snd_rawmidi_global_ops snd_ump_legacy_ops = {
574 	.dev_register = snd_ump_legacy_dev_register,
575 };
576 
577 static int process_legacy_output(struct snd_ump_endpoint *ump,
578 				 u32 *buffer, int count)
579 {
580 	struct snd_rawmidi_substream *substream;
581 	struct ump_cvt_to_ump *ctx;
582 	const int dir = SNDRV_RAWMIDI_STREAM_OUTPUT;
583 	unsigned char c;
584 	int group, size = 0;
585 	unsigned long flags;
586 
587 	if (!ump->out_cvts || !ump->legacy_out_opens)
588 		return 0;
589 
590 	spin_lock_irqsave(&ump->legacy_locks[dir], flags);
591 	for (group = 0; group < SNDRV_UMP_MAX_GROUPS; group++) {
592 		substream = ump->legacy_substreams[dir][group];
593 		if (!substream)
594 			continue;
595 		ctx = &ump->out_cvts[group];
596 		while (!ctx->ump_bytes &&
597 		       snd_rawmidi_transmit(substream, &c, 1) > 0)
598 			snd_ump_convert_to_ump(ump, group, c);
599 		if (ctx->ump_bytes && ctx->ump_bytes <= count) {
600 			size = ctx->ump_bytes;
601 			memcpy(buffer, ctx->ump, size);
602 			ctx->ump_bytes = 0;
603 			break;
604 		}
605 	}
606 	spin_unlock_irqrestore(&ump->legacy_locks[dir], flags);
607 	return size;
608 }
609 
610 static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src,
611 				 int words)
612 {
613 	struct snd_rawmidi_substream *substream;
614 	unsigned char buf[16];
615 	unsigned char group;
616 	unsigned long flags;
617 	const int dir = SNDRV_RAWMIDI_STREAM_INPUT;
618 	int size;
619 
620 	size = snd_ump_convert_from_ump(ump, src, buf, &group);
621 	if (size <= 0)
622 		return;
623 	spin_lock_irqsave(&ump->legacy_locks[dir], flags);
624 	substream = ump->legacy_substreams[dir][group];
625 	if (substream)
626 		snd_rawmidi_receive(substream, buf, size);
627 	spin_unlock_irqrestore(&ump->legacy_locks[dir], flags);
628 }
629 
630 int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
631 				  char *id, int device)
632 {
633 	struct snd_rawmidi *rmidi;
634 	bool input, output;
635 	int err;
636 
637 	err = snd_ump_convert_init(ump);
638 	if (err < 0)
639 		return err;
640 
641 	input = ump->core.info_flags & SNDRV_RAWMIDI_INFO_INPUT;
642 	output = ump->core.info_flags & SNDRV_RAWMIDI_INFO_OUTPUT;
643 	err = snd_rawmidi_new(ump->core.card, id, device,
644 			      output ? 16 : 0, input ? 16 : 0,
645 			      &rmidi);
646 	if (err < 0) {
647 		snd_ump_convert_free(ump);
648 		return err;
649 	}
650 
651 	if (input)
652 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
653 				    &snd_ump_legacy_input_ops);
654 	if (output)
655 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
656 				    &snd_ump_legacy_output_ops);
657 	rmidi->info_flags = ump->core.info_flags & ~SNDRV_RAWMIDI_INFO_UMP;
658 	rmidi->ops = &snd_ump_legacy_ops;
659 	rmidi->private_data = ump;
660 	ump->legacy_rmidi = rmidi;
661 	ump_dbg(ump, "Created a legacy rawmidi #%d (%s)\n", device, id);
662 	return 0;
663 }
664 EXPORT_SYMBOL_GPL(snd_ump_attach_legacy_rawmidi);
665 #endif /* CONFIG_SND_UMP_LEGACY_RAWMIDI */
666 
667 MODULE_DESCRIPTION("Universal MIDI Packet (UMP) Core Driver");
668 MODULE_LICENSE("GPL");
669