xref: /openbmc/linux/sound/core/ump.c (revision 9144f784f852f9a125cabe9927b986d909bfa439)
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 <sound/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 static void ump_handle_stream_msg(struct snd_ump_endpoint *ump,
34 				  const u32 *buf, int size);
35 #if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
36 static int process_legacy_output(struct snd_ump_endpoint *ump,
37 				 u32 *buffer, int count);
38 static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src,
39 				 int words);
40 static void update_legacy_names(struct snd_ump_endpoint *ump);
41 #else
process_legacy_output(struct snd_ump_endpoint * ump,u32 * buffer,int count)42 static inline int process_legacy_output(struct snd_ump_endpoint *ump,
43 					u32 *buffer, int count)
44 {
45 	return 0;
46 }
process_legacy_input(struct snd_ump_endpoint * ump,const u32 * src,int words)47 static inline void process_legacy_input(struct snd_ump_endpoint *ump,
48 					const u32 *src, int words)
49 {
50 }
update_legacy_names(struct snd_ump_endpoint * ump)51 static inline void update_legacy_names(struct snd_ump_endpoint *ump)
52 {
53 }
54 #endif
55 
56 static const struct snd_rawmidi_global_ops snd_ump_rawmidi_ops = {
57 	.dev_register = snd_ump_dev_register,
58 	.dev_unregister = snd_ump_dev_unregister,
59 	.ioctl = snd_ump_ioctl,
60 	.proc_read = snd_ump_proc_read,
61 };
62 
63 static const struct snd_rawmidi_ops snd_ump_rawmidi_input_ops = {
64 	.open = snd_ump_rawmidi_open,
65 	.close = snd_ump_rawmidi_close,
66 	.trigger = snd_ump_rawmidi_trigger,
67 };
68 
69 static const struct snd_rawmidi_ops snd_ump_rawmidi_output_ops = {
70 	.open = snd_ump_rawmidi_open,
71 	.close = snd_ump_rawmidi_close,
72 	.trigger = snd_ump_rawmidi_trigger,
73 	.drain = snd_ump_rawmidi_drain,
74 };
75 
snd_ump_endpoint_free(struct snd_rawmidi * rmidi)76 static void snd_ump_endpoint_free(struct snd_rawmidi *rmidi)
77 {
78 	struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
79 	struct snd_ump_block *fb;
80 
81 	while (!list_empty(&ump->block_list)) {
82 		fb = list_first_entry(&ump->block_list, struct snd_ump_block,
83 				      list);
84 		list_del(&fb->list);
85 		if (fb->private_free)
86 			fb->private_free(fb);
87 		kfree(fb);
88 	}
89 
90 	if (ump->private_free)
91 		ump->private_free(ump);
92 
93 #if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
94 	kfree(ump->out_cvts);
95 #endif
96 }
97 
98 /**
99  * snd_ump_endpoint_new - create a UMP Endpoint object
100  * @card: the card instance
101  * @id: the id string for rawmidi
102  * @device: the device index for rawmidi
103  * @output: 1 for enabling output
104  * @input: 1 for enabling input
105  * @ump_ret: the pointer to store the new UMP instance
106  *
107  * Creates a new UMP Endpoint object. A UMP Endpoint is tied with one rawmidi
108  * instance with one input and/or one output rawmidi stream (either uni-
109  * or bi-directional). A UMP Endpoint may contain one or multiple UMP Blocks
110  * that consist of one or multiple UMP Groups.
111  *
112  * Use snd_rawmidi_set_ops() to set the operators to the new instance.
113  * Unlike snd_rawmidi_new(), this function sets up the info_flags by itself
114  * depending on the given @output and @input.
115  *
116  * The device has SNDRV_RAWMIDI_INFO_UMP flag set and a different device
117  * file ("umpCxDx") than a standard MIDI 1.x device ("midiCxDx") is
118  * created.
119  *
120  * Return: Zero if successful, or a negative error code on failure.
121  */
snd_ump_endpoint_new(struct snd_card * card,char * id,int device,int output,int input,struct snd_ump_endpoint ** ump_ret)122 int snd_ump_endpoint_new(struct snd_card *card, char *id, int device,
123 			 int output, int input,
124 			 struct snd_ump_endpoint **ump_ret)
125 {
126 	unsigned int info_flags = SNDRV_RAWMIDI_INFO_UMP;
127 	struct snd_ump_endpoint *ump;
128 	int err;
129 
130 	if (input)
131 		info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
132 	if (output)
133 		info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
134 	if (input && output)
135 		info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
136 
137 	ump = kzalloc(sizeof(*ump), GFP_KERNEL);
138 	if (!ump)
139 		return -ENOMEM;
140 	INIT_LIST_HEAD(&ump->block_list);
141 	mutex_init(&ump->open_mutex);
142 	init_waitqueue_head(&ump->stream_wait);
143 #if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
144 	spin_lock_init(&ump->legacy_locks[0]);
145 	spin_lock_init(&ump->legacy_locks[1]);
146 #endif
147 	err = snd_rawmidi_init(&ump->core, card, id, device,
148 			       output, input, info_flags);
149 	if (err < 0) {
150 		snd_rawmidi_free(&ump->core);
151 		return err;
152 	}
153 
154 	ump->info.card = card->number;
155 	ump->info.device = device;
156 
157 	ump->core.private_free = snd_ump_endpoint_free;
158 	ump->core.ops = &snd_ump_rawmidi_ops;
159 	if (input)
160 		snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_INPUT,
161 				    &snd_ump_rawmidi_input_ops);
162 	if (output)
163 		snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_OUTPUT,
164 				    &snd_ump_rawmidi_output_ops);
165 
166 	ump_dbg(ump, "Created a UMP EP #%d (%s)\n", device, id);
167 	*ump_ret = ump;
168 	return 0;
169 }
170 EXPORT_SYMBOL_GPL(snd_ump_endpoint_new);
171 
172 /*
173  * Device register / unregister hooks;
174  *  do nothing, placeholders for avoiding the default rawmidi handling
175  */
176 
177 #if IS_ENABLED(CONFIG_SND_SEQUENCER)
snd_ump_dev_seq_free(struct snd_seq_device * device)178 static void snd_ump_dev_seq_free(struct snd_seq_device *device)
179 {
180 	struct snd_ump_endpoint *ump = device->private_data;
181 
182 	ump->seq_dev = NULL;
183 }
184 #endif
185 
snd_ump_dev_register(struct snd_rawmidi * rmidi)186 static int snd_ump_dev_register(struct snd_rawmidi *rmidi)
187 {
188 #if IS_ENABLED(CONFIG_SND_SEQUENCER)
189 	struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
190 	int err;
191 
192 	err = snd_seq_device_new(ump->core.card, ump->core.device,
193 				 SNDRV_SEQ_DEV_ID_UMP, 0, &ump->seq_dev);
194 	if (err < 0)
195 		return err;
196 	ump->seq_dev->private_data = ump;
197 	ump->seq_dev->private_free = snd_ump_dev_seq_free;
198 	snd_device_register(ump->core.card, ump->seq_dev);
199 #endif
200 	return 0;
201 }
202 
snd_ump_dev_unregister(struct snd_rawmidi * rmidi)203 static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi)
204 {
205 	return 0;
206 }
207 
208 static struct snd_ump_block *
snd_ump_get_block(struct snd_ump_endpoint * ump,unsigned char id)209 snd_ump_get_block(struct snd_ump_endpoint *ump, unsigned char id)
210 {
211 	struct snd_ump_block *fb;
212 
213 	list_for_each_entry(fb, &ump->block_list, list) {
214 		if (fb->info.block_id == id)
215 			return fb;
216 	}
217 	return NULL;
218 }
219 
220 /*
221  * rawmidi ops for UMP endpoint
222  */
snd_ump_rawmidi_open(struct snd_rawmidi_substream * substream)223 static int snd_ump_rawmidi_open(struct snd_rawmidi_substream *substream)
224 {
225 	struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
226 	int dir = substream->stream;
227 	int err;
228 
229 	if (ump->substreams[dir])
230 		return -EBUSY;
231 	err = ump->ops->open(ump, dir);
232 	if (err < 0)
233 		return err;
234 	ump->substreams[dir] = substream;
235 	return 0;
236 }
237 
snd_ump_rawmidi_close(struct snd_rawmidi_substream * substream)238 static int snd_ump_rawmidi_close(struct snd_rawmidi_substream *substream)
239 {
240 	struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
241 	int dir = substream->stream;
242 
243 	ump->substreams[dir] = NULL;
244 	ump->ops->close(ump, dir);
245 	return 0;
246 }
247 
snd_ump_rawmidi_trigger(struct snd_rawmidi_substream * substream,int up)248 static void snd_ump_rawmidi_trigger(struct snd_rawmidi_substream *substream,
249 				    int up)
250 {
251 	struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
252 	int dir = substream->stream;
253 
254 	ump->ops->trigger(ump, dir, up);
255 }
256 
snd_ump_rawmidi_drain(struct snd_rawmidi_substream * substream)257 static void snd_ump_rawmidi_drain(struct snd_rawmidi_substream *substream)
258 {
259 	struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
260 
261 	if (ump->ops->drain)
262 		ump->ops->drain(ump, SNDRV_RAWMIDI_STREAM_OUTPUT);
263 }
264 
265 /* number of 32bit words per message type */
266 static unsigned char ump_packet_words[0x10] = {
267 	1, 1, 1, 2, 2, 4, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4
268 };
269 
270 /**
271  * snd_ump_receive_ump_val - parse the UMP packet data
272  * @ump: UMP endpoint
273  * @val: UMP packet data
274  *
275  * The data is copied onto ump->input_buf[].
276  * When a full packet is completed, returns the number of words (from 1 to 4).
277  * OTOH, if the packet is incomplete, returns 0.
278  */
snd_ump_receive_ump_val(struct snd_ump_endpoint * ump,u32 val)279 int snd_ump_receive_ump_val(struct snd_ump_endpoint *ump, u32 val)
280 {
281 	int words;
282 
283 	if (!ump->input_pending)
284 		ump->input_pending = ump_packet_words[ump_message_type(val)];
285 
286 	ump->input_buf[ump->input_buf_head++] = val;
287 	ump->input_pending--;
288 	if (!ump->input_pending) {
289 		words = ump->input_buf_head;
290 		ump->input_buf_head = 0;
291 		return words;
292 	}
293 	return 0;
294 }
295 EXPORT_SYMBOL_GPL(snd_ump_receive_ump_val);
296 
297 /**
298  * snd_ump_receive - transfer UMP packets from the device
299  * @ump: the UMP endpoint
300  * @buffer: the buffer pointer to transfer
301  * @count: byte size to transfer
302  *
303  * Called from the driver to submit the received UMP packets from the device
304  * to user-space.  It's essentially a wrapper of rawmidi_receive().
305  * The data to receive is in CPU-native endianness.
306  */
snd_ump_receive(struct snd_ump_endpoint * ump,const u32 * buffer,int count)307 int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count)
308 {
309 	struct snd_rawmidi_substream *substream;
310 	const u32 *p = buffer;
311 	int n, words = count >> 2;
312 
313 	while (words--) {
314 		n = snd_ump_receive_ump_val(ump, *p++);
315 		if (!n)
316 			continue;
317 		ump_handle_stream_msg(ump, ump->input_buf, n);
318 #if IS_ENABLED(CONFIG_SND_SEQUENCER)
319 		if (ump->seq_ops)
320 			ump->seq_ops->input_receive(ump, ump->input_buf, n);
321 #endif
322 		process_legacy_input(ump, ump->input_buf, n);
323 	}
324 
325 	substream = ump->substreams[SNDRV_RAWMIDI_STREAM_INPUT];
326 	if (!substream)
327 		return 0;
328 	return snd_rawmidi_receive(substream, (const char *)buffer, count);
329 }
330 EXPORT_SYMBOL_GPL(snd_ump_receive);
331 
332 /**
333  * snd_ump_transmit - transmit UMP packets
334  * @ump: the UMP endpoint
335  * @buffer: the buffer pointer to transfer
336  * @count: byte size to transfer
337  *
338  * Called from the driver to obtain the UMP packets from user-space to the
339  * device.  It's essentially a wrapper of rawmidi_transmit().
340  * The data to transmit is in CPU-native endianness.
341  */
snd_ump_transmit(struct snd_ump_endpoint * ump,u32 * buffer,int count)342 int snd_ump_transmit(struct snd_ump_endpoint *ump, u32 *buffer, int count)
343 {
344 	struct snd_rawmidi_substream *substream =
345 		ump->substreams[SNDRV_RAWMIDI_STREAM_OUTPUT];
346 	int err;
347 
348 	if (!substream)
349 		return -ENODEV;
350 	err = snd_rawmidi_transmit(substream, (char *)buffer, count);
351 	/* received either data or an error? */
352 	if (err)
353 		return err;
354 	return process_legacy_output(ump, buffer, count);
355 }
356 EXPORT_SYMBOL_GPL(snd_ump_transmit);
357 
358 /**
359  * snd_ump_block_new - Create a UMP block
360  * @ump: UMP object
361  * @blk: block ID number to create
362  * @direction: direction (in/out/bidirection)
363  * @first_group: the first group ID (0-based)
364  * @num_groups: the number of groups in this block
365  * @blk_ret: the pointer to store the resultant block object
366  */
snd_ump_block_new(struct snd_ump_endpoint * ump,unsigned int blk,unsigned int direction,unsigned int first_group,unsigned int num_groups,struct snd_ump_block ** blk_ret)367 int snd_ump_block_new(struct snd_ump_endpoint *ump, unsigned int blk,
368 		      unsigned int direction, unsigned int first_group,
369 		      unsigned int num_groups, struct snd_ump_block **blk_ret)
370 {
371 	struct snd_ump_block *fb, *p;
372 
373 	if (blk < 0 || blk >= SNDRV_UMP_MAX_BLOCKS)
374 		return -EINVAL;
375 
376 	if (snd_ump_get_block(ump, blk))
377 		return -EBUSY;
378 
379 	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
380 	if (!fb)
381 		return -ENOMEM;
382 
383 	fb->ump = ump;
384 	fb->info.card = ump->info.card;
385 	fb->info.device = ump->info.device;
386 	fb->info.block_id = blk;
387 	if (blk >= ump->info.num_blocks)
388 		ump->info.num_blocks = blk + 1;
389 	fb->info.direction = direction;
390 	fb->info.active = 1;
391 	fb->info.first_group = first_group;
392 	fb->info.num_groups = num_groups;
393 	/* fill the default name, may be overwritten to a better name */
394 	snprintf(fb->info.name, sizeof(fb->info.name), "Group %d-%d",
395 		 first_group + 1, first_group + num_groups);
396 
397 	/* put the entry in the ordered list */
398 	list_for_each_entry(p, &ump->block_list, list) {
399 		if (p->info.block_id > blk) {
400 			list_add_tail(&fb->list, &p->list);
401 			goto added;
402 		}
403 	}
404 	list_add_tail(&fb->list, &ump->block_list);
405 
406  added:
407 	ump_dbg(ump, "Created a UMP Block #%d (%s)\n", blk, fb->info.name);
408 	*blk_ret = fb;
409 	return 0;
410 }
411 EXPORT_SYMBOL_GPL(snd_ump_block_new);
412 
snd_ump_ioctl_block(struct snd_ump_endpoint * ump,struct snd_ump_block_info __user * argp)413 static int snd_ump_ioctl_block(struct snd_ump_endpoint *ump,
414 			       struct snd_ump_block_info __user *argp)
415 {
416 	struct snd_ump_block *fb;
417 	unsigned char id;
418 
419 	if (get_user(id, &argp->block_id))
420 		return -EFAULT;
421 	fb = snd_ump_get_block(ump, id);
422 	if (!fb)
423 		return -ENOENT;
424 	if (copy_to_user(argp, &fb->info, sizeof(fb->info)))
425 		return -EFAULT;
426 	return 0;
427 }
428 
429 /*
430  * Handle UMP-specific ioctls; called from snd_rawmidi_ioctl()
431  */
snd_ump_ioctl(struct snd_rawmidi * rmidi,unsigned int cmd,void __user * argp)432 static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd,
433 			  void __user *argp)
434 {
435 	struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
436 
437 	switch (cmd) {
438 	case SNDRV_UMP_IOCTL_ENDPOINT_INFO:
439 		if (copy_to_user(argp, &ump->info, sizeof(ump->info)))
440 			return -EFAULT;
441 		return 0;
442 	case SNDRV_UMP_IOCTL_BLOCK_INFO:
443 		return snd_ump_ioctl_block(ump, argp);
444 	default:
445 		ump_dbg(ump, "rawmidi: unknown command = 0x%x\n", cmd);
446 		return -ENOTTY;
447 	}
448 }
449 
ump_direction_string(int dir)450 static const char *ump_direction_string(int dir)
451 {
452 	switch (dir) {
453 	case SNDRV_UMP_DIR_INPUT:
454 		return "input";
455 	case SNDRV_UMP_DIR_OUTPUT:
456 		return "output";
457 	case SNDRV_UMP_DIR_BIDIRECTION:
458 		return "bidirection";
459 	default:
460 		return "unknown";
461 	}
462 }
463 
ump_ui_hint_string(int dir)464 static const char *ump_ui_hint_string(int dir)
465 {
466 	switch (dir) {
467 	case  SNDRV_UMP_BLOCK_UI_HINT_RECEIVER:
468 		return "receiver";
469 	case SNDRV_UMP_BLOCK_UI_HINT_SENDER:
470 		return "sender";
471 	case SNDRV_UMP_BLOCK_UI_HINT_BOTH:
472 		return "both";
473 	default:
474 		return "unknown";
475 	}
476 }
477 
478 /* Additional proc file output */
snd_ump_proc_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)479 static void snd_ump_proc_read(struct snd_info_entry *entry,
480 			      struct snd_info_buffer *buffer)
481 {
482 	struct snd_rawmidi *rmidi = entry->private_data;
483 	struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
484 	struct snd_ump_block *fb;
485 
486 	snd_iprintf(buffer, "EP Name: %s\n", ump->info.name);
487 	snd_iprintf(buffer, "EP Product ID: %s\n", ump->info.product_id);
488 	snd_iprintf(buffer, "UMP Version: 0x%04x\n", ump->info.version);
489 	snd_iprintf(buffer, "Protocol Caps: 0x%08x\n", ump->info.protocol_caps);
490 	snd_iprintf(buffer, "Protocol: 0x%08x\n", ump->info.protocol);
491 	if (ump->info.version) {
492 		snd_iprintf(buffer, "Manufacturer ID: 0x%08x\n",
493 			    ump->info.manufacturer_id);
494 		snd_iprintf(buffer, "Family ID: 0x%04x\n", ump->info.family_id);
495 		snd_iprintf(buffer, "Model ID: 0x%04x\n", ump->info.model_id);
496 		snd_iprintf(buffer, "SW Revision: 0x%02x%02x%02x%02x\n",
497 			    ump->info.sw_revision[0],
498 			    ump->info.sw_revision[1],
499 			    ump->info.sw_revision[2],
500 			    ump->info.sw_revision[3]);
501 	}
502 	snd_iprintf(buffer, "Static Blocks: %s\n",
503 		    (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) ? "Yes" : "No");
504 	snd_iprintf(buffer, "Num Blocks: %d\n\n", ump->info.num_blocks);
505 
506 	list_for_each_entry(fb, &ump->block_list, list) {
507 		snd_iprintf(buffer, "Block %d (%s)\n", fb->info.block_id,
508 			    fb->info.name);
509 		snd_iprintf(buffer, "  Direction: %s\n",
510 			    ump_direction_string(fb->info.direction));
511 		snd_iprintf(buffer, "  Active: %s\n",
512 			    fb->info.active ? "Yes" : "No");
513 		snd_iprintf(buffer, "  Groups: %d-%d\n",
514 			    fb->info.first_group + 1,
515 			    fb->info.first_group + fb->info.num_groups);
516 		snd_iprintf(buffer, "  Is MIDI1: %s%s\n",
517 			    (fb->info.flags & SNDRV_UMP_BLOCK_IS_MIDI1) ? "Yes" : "No",
518 			    (fb->info.flags & SNDRV_UMP_BLOCK_IS_LOWSPEED) ? " (Low Speed)" : "");
519 		if (ump->info.version) {
520 			snd_iprintf(buffer, "  MIDI-CI Version: %d\n",
521 				    fb->info.midi_ci_version);
522 			snd_iprintf(buffer, "  Sysex8 Streams: %d\n",
523 				    fb->info.sysex8_streams);
524 			snd_iprintf(buffer, "  UI Hint: %s\n",
525 				    ump_ui_hint_string(fb->info.ui_hint));
526 		}
527 		snd_iprintf(buffer, "\n");
528 	}
529 }
530 
531 /* update dir_bits and active flag for all groups in the client */
snd_ump_update_group_attrs(struct snd_ump_endpoint * ump)532 void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump)
533 {
534 	struct snd_ump_block *fb;
535 	struct snd_ump_group *group;
536 	int i;
537 
538 	for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) {
539 		group = &ump->groups[i];
540 		*group->name = 0;
541 		group->dir_bits = 0;
542 		group->active = 0;
543 		group->group = i;
544 		group->valid = false;
545 	}
546 
547 	list_for_each_entry(fb, &ump->block_list, list) {
548 		if (fb->info.first_group + fb->info.num_groups > SNDRV_UMP_MAX_GROUPS)
549 			break;
550 		group = &ump->groups[fb->info.first_group];
551 		for (i = 0; i < fb->info.num_groups; i++, group++) {
552 			group->valid = true;
553 			if (fb->info.active)
554 				group->active = 1;
555 			switch (fb->info.direction) {
556 			case SNDRV_UMP_DIR_INPUT:
557 				group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_INPUT);
558 				break;
559 			case SNDRV_UMP_DIR_OUTPUT:
560 				group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_OUTPUT);
561 				break;
562 			case SNDRV_UMP_DIR_BIDIRECTION:
563 				group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_INPUT) |
564 					(1 << SNDRV_RAWMIDI_STREAM_OUTPUT);
565 				break;
566 			}
567 			if (!*fb->info.name)
568 				continue;
569 			if (!*group->name) {
570 				/* store the first matching name */
571 				strscpy(group->name, fb->info.name,
572 					sizeof(group->name));
573 			} else {
574 				/* when overlapping, concat names */
575 				strlcat(group->name, ", ", sizeof(group->name));
576 				strlcat(group->name, fb->info.name,
577 					sizeof(group->name));
578 			}
579 		}
580 	}
581 }
582 EXPORT_SYMBOL_GPL(snd_ump_update_group_attrs);
583 
584 /*
585  * UMP endpoint and function block handling
586  */
587 
588 /* open / close UMP streams for the internal stream msg communication */
ump_request_open(struct snd_ump_endpoint * ump)589 static int ump_request_open(struct snd_ump_endpoint *ump)
590 {
591 	return snd_rawmidi_kernel_open(&ump->core, 0,
592 				       SNDRV_RAWMIDI_LFLG_OUTPUT,
593 				       &ump->stream_rfile);
594 }
595 
ump_request_close(struct snd_ump_endpoint * ump)596 static void ump_request_close(struct snd_ump_endpoint *ump)
597 {
598 	snd_rawmidi_kernel_release(&ump->stream_rfile);
599 }
600 
601 /* request a command and wait for the given response;
602  * @req1 and @req2 are u32 commands
603  * @reply is the expected UMP stream status
604  */
ump_req_msg(struct snd_ump_endpoint * ump,u32 req1,u32 req2,u32 reply)605 static int ump_req_msg(struct snd_ump_endpoint *ump, u32 req1, u32 req2,
606 		       u32 reply)
607 {
608 	u32 buf[4];
609 
610 	ump_dbg(ump, "%s: request %08x %08x, wait-for %08x\n",
611 		__func__, req1, req2, reply);
612 	memset(buf, 0, sizeof(buf));
613 	buf[0] = req1;
614 	buf[1] = req2;
615 	ump->stream_finished = 0;
616 	ump->stream_wait_for = reply;
617 	snd_rawmidi_kernel_write(ump->stream_rfile.output,
618 				 (unsigned char *)&buf, 16);
619 	wait_event_timeout(ump->stream_wait, ump->stream_finished,
620 			   msecs_to_jiffies(500));
621 	if (!READ_ONCE(ump->stream_finished)) {
622 		ump_dbg(ump, "%s: request timed out\n", __func__);
623 		return -ETIMEDOUT;
624 	}
625 	ump->stream_finished = 0;
626 	ump_dbg(ump, "%s: reply: %08x %08x %08x %08x\n",
627 		__func__, buf[0], buf[1], buf[2], buf[3]);
628 	return 0;
629 }
630 
631 /* append the received letters via UMP packet to the given string buffer;
632  * return 1 if the full string is received or 0 to continue
633  */
ump_append_string(struct snd_ump_endpoint * ump,char * dest,int maxsize,const u32 * buf,int offset)634 static int ump_append_string(struct snd_ump_endpoint *ump, char *dest,
635 			     int maxsize, const u32 *buf, int offset)
636 {
637 	unsigned char format;
638 	int c;
639 
640 	format = ump_stream_message_format(buf[0]);
641 	if (format == UMP_STREAM_MSG_FORMAT_SINGLE ||
642 	    format == UMP_STREAM_MSG_FORMAT_START) {
643 		c = 0;
644 	} else {
645 		c = strlen(dest);
646 		if (c >= maxsize - 1)
647 			return 1;
648 	}
649 
650 	for (; offset < 16; offset++) {
651 		dest[c] = buf[offset / 4] >> (3 - (offset % 4)) * 8;
652 		if (!dest[c])
653 			break;
654 		if (++c >= maxsize - 1)
655 			break;
656 	}
657 	dest[c] = 0;
658 	return (format == UMP_STREAM_MSG_FORMAT_SINGLE ||
659 		format == UMP_STREAM_MSG_FORMAT_END);
660 }
661 
662 /* handle EP info stream message; update the UMP attributes */
ump_handle_ep_info_msg(struct snd_ump_endpoint * ump,const union snd_ump_stream_msg * buf)663 static int ump_handle_ep_info_msg(struct snd_ump_endpoint *ump,
664 				  const union snd_ump_stream_msg *buf)
665 {
666 	ump->info.version = (buf->ep_info.ump_version_major << 8) |
667 		buf->ep_info.ump_version_minor;
668 	ump->info.num_blocks = buf->ep_info.num_function_blocks;
669 	if (ump->info.num_blocks > SNDRV_UMP_MAX_BLOCKS) {
670 		ump_info(ump, "Invalid function blocks %d, fallback to 1\n",
671 			 ump->info.num_blocks);
672 		ump->info.num_blocks = 1;
673 	}
674 
675 	if (buf->ep_info.static_function_block)
676 		ump->info.flags |= SNDRV_UMP_EP_INFO_STATIC_BLOCKS;
677 
678 	ump->info.protocol_caps = (buf->ep_info.protocol << 8) |
679 		buf->ep_info.jrts;
680 
681 	ump_dbg(ump, "EP info: version=%x, num_blocks=%x, proto_caps=%x\n",
682 		ump->info.version, ump->info.num_blocks, ump->info.protocol_caps);
683 	return 1; /* finished */
684 }
685 
686 /* handle EP device info stream message; update the UMP attributes */
ump_handle_device_info_msg(struct snd_ump_endpoint * ump,const union snd_ump_stream_msg * buf)687 static int ump_handle_device_info_msg(struct snd_ump_endpoint *ump,
688 				      const union snd_ump_stream_msg *buf)
689 {
690 	ump->info.manufacturer_id = buf->device_info.manufacture_id & 0x7f7f7f;
691 	ump->info.family_id = (buf->device_info.family_msb << 8) |
692 		buf->device_info.family_lsb;
693 	ump->info.model_id = (buf->device_info.model_msb << 8) |
694 		buf->device_info.model_lsb;
695 	ump->info.sw_revision[0] = (buf->device_info.sw_revision >> 24) & 0x7f;
696 	ump->info.sw_revision[1] = (buf->device_info.sw_revision >> 16) & 0x7f;
697 	ump->info.sw_revision[2] = (buf->device_info.sw_revision >> 8) & 0x7f;
698 	ump->info.sw_revision[3] = buf->device_info.sw_revision & 0x7f;
699 	ump_dbg(ump, "EP devinfo: manid=%08x, family=%04x, model=%04x, sw=%02x%02x%02x%02x\n",
700 		ump->info.manufacturer_id,
701 		ump->info.family_id,
702 		ump->info.model_id,
703 		ump->info.sw_revision[0],
704 		ump->info.sw_revision[1],
705 		ump->info.sw_revision[2],
706 		ump->info.sw_revision[3]);
707 	return 1; /* finished */
708 }
709 
710 /* handle EP name stream message; update the UMP name string */
ump_handle_ep_name_msg(struct snd_ump_endpoint * ump,const union snd_ump_stream_msg * buf)711 static int ump_handle_ep_name_msg(struct snd_ump_endpoint *ump,
712 				  const union snd_ump_stream_msg *buf)
713 {
714 	return ump_append_string(ump, ump->info.name, sizeof(ump->info.name),
715 				 buf->raw, 2);
716 }
717 
718 /* handle EP product id stream message; update the UMP product_id string */
ump_handle_product_id_msg(struct snd_ump_endpoint * ump,const union snd_ump_stream_msg * buf)719 static int ump_handle_product_id_msg(struct snd_ump_endpoint *ump,
720 				     const union snd_ump_stream_msg *buf)
721 {
722 	return ump_append_string(ump, ump->info.product_id,
723 				 sizeof(ump->info.product_id),
724 				 buf->raw, 2);
725 }
726 
727 /* notify the protocol change to sequencer */
seq_notify_protocol(struct snd_ump_endpoint * ump)728 static void seq_notify_protocol(struct snd_ump_endpoint *ump)
729 {
730 #if IS_ENABLED(CONFIG_SND_SEQUENCER)
731 	if (ump->seq_ops && ump->seq_ops->switch_protocol)
732 		ump->seq_ops->switch_protocol(ump);
733 #endif /* CONFIG_SND_SEQUENCER */
734 }
735 
736 /**
737  * snd_ump_switch_protocol - switch MIDI protocol
738  * @ump: UMP endpoint
739  * @protocol: protocol to switch to
740  *
741  * Returns 1 if the protocol is actually switched, 0 if unchanged
742  */
snd_ump_switch_protocol(struct snd_ump_endpoint * ump,unsigned int protocol)743 int snd_ump_switch_protocol(struct snd_ump_endpoint *ump, unsigned int protocol)
744 {
745 	unsigned int type;
746 
747 	protocol &= ump->info.protocol_caps;
748 	if (protocol == ump->info.protocol)
749 		return 0;
750 
751 	type = protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK;
752 	if (type != SNDRV_UMP_EP_INFO_PROTO_MIDI1 &&
753 	    type != SNDRV_UMP_EP_INFO_PROTO_MIDI2)
754 		return 0;
755 
756 	ump->info.protocol = protocol;
757 	ump_dbg(ump, "New protocol = %x (caps = %x)\n",
758 		protocol, ump->info.protocol_caps);
759 	seq_notify_protocol(ump);
760 	return 1;
761 }
762 EXPORT_SYMBOL_GPL(snd_ump_switch_protocol);
763 
764 /* handle EP stream config message; update the UMP protocol */
ump_handle_stream_cfg_msg(struct snd_ump_endpoint * ump,const union snd_ump_stream_msg * buf)765 static int ump_handle_stream_cfg_msg(struct snd_ump_endpoint *ump,
766 				     const union snd_ump_stream_msg *buf)
767 {
768 	unsigned int protocol =
769 		(buf->stream_cfg.protocol << 8) | buf->stream_cfg.jrts;
770 
771 	snd_ump_switch_protocol(ump, protocol);
772 	return 1; /* finished */
773 }
774 
775 /* Extract Function Block info from UMP packet */
fill_fb_info(struct snd_ump_endpoint * ump,struct snd_ump_block_info * info,const union snd_ump_stream_msg * buf)776 static void fill_fb_info(struct snd_ump_endpoint *ump,
777 			 struct snd_ump_block_info *info,
778 			 const union snd_ump_stream_msg *buf)
779 {
780 	info->direction = buf->fb_info.direction;
781 	info->ui_hint = buf->fb_info.ui_hint;
782 	info->first_group = buf->fb_info.first_group;
783 	info->num_groups = buf->fb_info.num_groups;
784 	if (buf->fb_info.midi_10 < 2)
785 		info->flags = buf->fb_info.midi_10;
786 	else
787 		info->flags = SNDRV_UMP_BLOCK_IS_MIDI1 | SNDRV_UMP_BLOCK_IS_LOWSPEED;
788 	info->active = buf->fb_info.active;
789 	info->midi_ci_version = buf->fb_info.midi_ci_version;
790 	info->sysex8_streams = buf->fb_info.sysex8_streams;
791 
792 	ump_dbg(ump, "FB %d: dir=%d, active=%d, first_gp=%d, num_gp=%d, midici=%d, sysex8=%d, flags=0x%x\n",
793 		info->block_id, info->direction, info->active,
794 		info->first_group, info->num_groups, info->midi_ci_version,
795 		info->sysex8_streams, info->flags);
796 
797 	if ((info->flags & SNDRV_UMP_BLOCK_IS_MIDI1) && info->num_groups != 1) {
798 		info->num_groups = 1;
799 		ump_dbg(ump, "FB %d: corrected groups to 1 for MIDI1\n",
800 			info->block_id);
801 	}
802 }
803 
804 /* check whether the FB info gets updated by the current message */
is_fb_info_updated(struct snd_ump_endpoint * ump,struct snd_ump_block * fb,const union snd_ump_stream_msg * buf)805 static bool is_fb_info_updated(struct snd_ump_endpoint *ump,
806 			       struct snd_ump_block *fb,
807 			       const union snd_ump_stream_msg *buf)
808 {
809 	char tmpbuf[offsetof(struct snd_ump_block_info, name)];
810 
811 	if (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) {
812 		ump_info(ump, "Skipping static FB info update (blk#%d)\n",
813 			 fb->info.block_id);
814 		return 0;
815 	}
816 
817 	memcpy(tmpbuf, &fb->info, sizeof(tmpbuf));
818 	fill_fb_info(ump, (struct snd_ump_block_info *)tmpbuf, buf);
819 	return memcmp(&fb->info, tmpbuf, sizeof(tmpbuf)) != 0;
820 }
821 
822 /* notify the FB info/name change to sequencer */
seq_notify_fb_change(struct snd_ump_endpoint * ump,struct snd_ump_block * fb)823 static void seq_notify_fb_change(struct snd_ump_endpoint *ump,
824 				 struct snd_ump_block *fb)
825 {
826 #if IS_ENABLED(CONFIG_SND_SEQUENCER)
827 	if (ump->seq_ops && ump->seq_ops->notify_fb_change)
828 		ump->seq_ops->notify_fb_change(ump, fb);
829 #endif
830 }
831 
832 /* handle FB info message; update FB info if the block is present */
ump_handle_fb_info_msg(struct snd_ump_endpoint * ump,const union snd_ump_stream_msg * buf)833 static int ump_handle_fb_info_msg(struct snd_ump_endpoint *ump,
834 				  const union snd_ump_stream_msg *buf)
835 {
836 	unsigned char blk;
837 	struct snd_ump_block *fb;
838 
839 	blk = buf->fb_info.function_block_id;
840 	fb = snd_ump_get_block(ump, blk);
841 
842 	/* complain only if updated after parsing */
843 	if (!fb && ump->parsed) {
844 		ump_info(ump, "Function Block Info Update for non-existing block %d\n",
845 			 blk);
846 		return -ENODEV;
847 	}
848 
849 	/* When updated after the initial parse, check the FB info update */
850 	if (ump->parsed && !is_fb_info_updated(ump, fb, buf))
851 		return 1; /* no content change */
852 
853 	if (fb) {
854 		fill_fb_info(ump, &fb->info, buf);
855 		if (ump->parsed) {
856 			snd_ump_update_group_attrs(ump);
857 			update_legacy_names(ump);
858 			seq_notify_fb_change(ump, fb);
859 		}
860 	}
861 
862 	return 1; /* finished */
863 }
864 
865 /* handle FB name message; update the FB name string */
ump_handle_fb_name_msg(struct snd_ump_endpoint * ump,const union snd_ump_stream_msg * buf)866 static int ump_handle_fb_name_msg(struct snd_ump_endpoint *ump,
867 				  const union snd_ump_stream_msg *buf)
868 {
869 	unsigned char blk;
870 	struct snd_ump_block *fb;
871 	int ret;
872 
873 	blk = buf->fb_name.function_block_id;
874 	fb = snd_ump_get_block(ump, blk);
875 	if (!fb)
876 		return -ENODEV;
877 
878 	if (ump->parsed &&
879 	    (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS)) {
880 		ump_dbg(ump, "Skipping static FB name update (blk#%d)\n",
881 			fb->info.block_id);
882 		return 0;
883 	}
884 
885 	ret = ump_append_string(ump, fb->info.name, sizeof(fb->info.name),
886 				buf->raw, 3);
887 	/* notify the FB name update to sequencer, too */
888 	if (ret > 0 && ump->parsed) {
889 		snd_ump_update_group_attrs(ump);
890 		update_legacy_names(ump);
891 		seq_notify_fb_change(ump, fb);
892 	}
893 	return ret;
894 }
895 
create_block_from_fb_info(struct snd_ump_endpoint * ump,int blk)896 static int create_block_from_fb_info(struct snd_ump_endpoint *ump, int blk)
897 {
898 	struct snd_ump_block *fb;
899 	unsigned char direction, first_group, num_groups;
900 	const union snd_ump_stream_msg *buf =
901 		(const union snd_ump_stream_msg *)ump->input_buf;
902 	u32 msg;
903 	int err;
904 
905 	/* query the FB info once */
906 	msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_FB_DISCOVERY, 0) |
907 		(blk << 8) | UMP_STREAM_MSG_REQUEST_FB_INFO;
908 	err = ump_req_msg(ump, msg, 0, UMP_STREAM_MSG_STATUS_FB_INFO);
909 	if (err < 0) {
910 		ump_dbg(ump, "Unable to get FB info for block %d\n", blk);
911 		return err;
912 	}
913 
914 	/* the last input must be the FB info */
915 	if (buf->fb_info.status != UMP_STREAM_MSG_STATUS_FB_INFO) {
916 		ump_dbg(ump, "Inconsistent input: 0x%x\n", *buf->raw);
917 		return -EINVAL;
918 	}
919 
920 	direction = buf->fb_info.direction;
921 	first_group = buf->fb_info.first_group;
922 	num_groups = buf->fb_info.num_groups;
923 
924 	err = snd_ump_block_new(ump, blk, direction, first_group, num_groups,
925 				&fb);
926 	if (err < 0)
927 		return err;
928 
929 	fill_fb_info(ump, &fb->info, buf);
930 
931 	msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_FB_DISCOVERY, 0) |
932 		(blk << 8) | UMP_STREAM_MSG_REQUEST_FB_NAME;
933 	err = ump_req_msg(ump, msg, 0, UMP_STREAM_MSG_STATUS_FB_NAME);
934 	if (err)
935 		ump_dbg(ump, "Unable to get UMP FB name string #%d\n", blk);
936 
937 	return 0;
938 }
939 
940 /* handle stream messages, called from snd_ump_receive() */
ump_handle_stream_msg(struct snd_ump_endpoint * ump,const u32 * buf,int size)941 static void ump_handle_stream_msg(struct snd_ump_endpoint *ump,
942 				  const u32 *buf, int size)
943 {
944 	const union snd_ump_stream_msg *msg;
945 	unsigned int status;
946 	int ret;
947 
948 	/* UMP stream message suppressed (for gadget UMP)? */
949 	if (ump->no_process_stream)
950 		return;
951 
952 	BUILD_BUG_ON(sizeof(*msg) != 16);
953 	ump_dbg(ump, "Stream msg: %08x %08x %08x %08x\n",
954 		buf[0], buf[1], buf[2], buf[3]);
955 
956 	if (size != 4 || ump_message_type(*buf) != UMP_MSG_TYPE_STREAM)
957 		return;
958 
959 	msg = (const union snd_ump_stream_msg *)buf;
960 	status = ump_stream_message_status(*buf);
961 	switch (status) {
962 	case UMP_STREAM_MSG_STATUS_EP_INFO:
963 		ret = ump_handle_ep_info_msg(ump, msg);
964 		break;
965 	case UMP_STREAM_MSG_STATUS_DEVICE_INFO:
966 		ret = ump_handle_device_info_msg(ump, msg);
967 		break;
968 	case UMP_STREAM_MSG_STATUS_EP_NAME:
969 		ret = ump_handle_ep_name_msg(ump, msg);
970 		break;
971 	case UMP_STREAM_MSG_STATUS_PRODUCT_ID:
972 		ret = ump_handle_product_id_msg(ump, msg);
973 		break;
974 	case UMP_STREAM_MSG_STATUS_STREAM_CFG:
975 		ret = ump_handle_stream_cfg_msg(ump, msg);
976 		break;
977 	case UMP_STREAM_MSG_STATUS_FB_INFO:
978 		ret = ump_handle_fb_info_msg(ump, msg);
979 		break;
980 	case UMP_STREAM_MSG_STATUS_FB_NAME:
981 		ret = ump_handle_fb_name_msg(ump, msg);
982 		break;
983 	default:
984 		return;
985 	}
986 
987 	/* when the message has been processed fully, wake up */
988 	if (ret > 0 && ump->stream_wait_for == status) {
989 		WRITE_ONCE(ump->stream_finished, 1);
990 		wake_up(&ump->stream_wait);
991 	}
992 }
993 
994 /**
995  * snd_ump_parse_endpoint - parse endpoint and create function blocks
996  * @ump: UMP object
997  *
998  * Returns 0 for successful parse, -ENODEV if device doesn't respond
999  * (or the query is unsupported), or other error code for serious errors.
1000  */
snd_ump_parse_endpoint(struct snd_ump_endpoint * ump)1001 int snd_ump_parse_endpoint(struct snd_ump_endpoint *ump)
1002 {
1003 	int blk, err;
1004 	u32 msg;
1005 
1006 	if (!(ump->core.info_flags & SNDRV_RAWMIDI_INFO_DUPLEX))
1007 		return -ENODEV;
1008 
1009 	err = ump_request_open(ump);
1010 	if (err < 0) {
1011 		ump_dbg(ump, "Unable to open rawmidi device: %d\n", err);
1012 		return err;
1013 	}
1014 
1015 	/* Check Endpoint Information */
1016 	msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_EP_DISCOVERY, 0) |
1017 		0x0101; /* UMP version 1.1 */
1018 	err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_EP_INFO,
1019 			  UMP_STREAM_MSG_STATUS_EP_INFO);
1020 	if (err < 0) {
1021 		ump_dbg(ump, "Unable to get UMP EP info\n");
1022 		goto error;
1023 	}
1024 
1025 	/* Request Endpoint Device Info */
1026 	err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_DEVICE_INFO,
1027 			  UMP_STREAM_MSG_STATUS_DEVICE_INFO);
1028 	if (err < 0)
1029 		ump_dbg(ump, "Unable to get UMP EP device info\n");
1030 
1031 	/* Request Endpoint Name */
1032 	err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_EP_NAME,
1033 			  UMP_STREAM_MSG_STATUS_EP_NAME);
1034 	if (err < 0)
1035 		ump_dbg(ump, "Unable to get UMP EP name string\n");
1036 
1037 	/* Request Endpoint Product ID */
1038 	err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_PRODUCT_ID,
1039 			  UMP_STREAM_MSG_STATUS_PRODUCT_ID);
1040 	if (err < 0)
1041 		ump_dbg(ump, "Unable to get UMP EP product ID string\n");
1042 
1043 	/* Get the current stream configuration */
1044 	err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_STREAM_CFG,
1045 			  UMP_STREAM_MSG_STATUS_STREAM_CFG);
1046 	if (err < 0)
1047 		ump_dbg(ump, "Unable to get UMP EP stream config\n");
1048 
1049 	/* If no protocol is set by some reason, assume the valid one */
1050 	if (!(ump->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK)) {
1051 		if (ump->info.protocol_caps & SNDRV_UMP_EP_INFO_PROTO_MIDI2)
1052 			ump->info.protocol |= SNDRV_UMP_EP_INFO_PROTO_MIDI2;
1053 		else if (ump->info.protocol_caps & SNDRV_UMP_EP_INFO_PROTO_MIDI1)
1054 			ump->info.protocol |= SNDRV_UMP_EP_INFO_PROTO_MIDI1;
1055 	}
1056 
1057 	/* Query and create blocks from Function Blocks */
1058 	for (blk = 0; blk < ump->info.num_blocks; blk++) {
1059 		err = create_block_from_fb_info(ump, blk);
1060 		if (err < 0)
1061 			continue;
1062 	}
1063 
1064 	/* initialize group attributions */
1065 	snd_ump_update_group_attrs(ump);
1066 
1067  error:
1068 	ump->parsed = true;
1069 	ump_request_close(ump);
1070 	if (err == -ETIMEDOUT)
1071 		err = -ENODEV;
1072 	return err;
1073 }
1074 EXPORT_SYMBOL_GPL(snd_ump_parse_endpoint);
1075 
1076 #if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
1077 /*
1078  * Legacy rawmidi support
1079  */
snd_ump_legacy_open(struct snd_rawmidi_substream * substream)1080 static int snd_ump_legacy_open(struct snd_rawmidi_substream *substream)
1081 {
1082 	struct snd_ump_endpoint *ump = substream->rmidi->private_data;
1083 	int dir = substream->stream;
1084 	int group = ump->legacy_mapping[substream->number];
1085 	int err;
1086 
1087 	guard(mutex)(&ump->open_mutex);
1088 	if (ump->legacy_substreams[dir][group])
1089 		return -EBUSY;
1090 	if (!ump->groups[group].active)
1091 		return -ENODEV;
1092 	if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
1093 		if (!ump->legacy_out_opens) {
1094 			err = snd_rawmidi_kernel_open(&ump->core, 0,
1095 						      SNDRV_RAWMIDI_LFLG_OUTPUT |
1096 						      SNDRV_RAWMIDI_LFLG_APPEND,
1097 						      &ump->legacy_out_rfile);
1098 			if (err < 0)
1099 				return err;
1100 		}
1101 		ump->legacy_out_opens++;
1102 		snd_ump_convert_reset(&ump->out_cvts[group]);
1103 	}
1104 	guard(spinlock_irq)(&ump->legacy_locks[dir]);
1105 	ump->legacy_substreams[dir][group] = substream;
1106 	return 0;
1107 }
1108 
snd_ump_legacy_close(struct snd_rawmidi_substream * substream)1109 static int snd_ump_legacy_close(struct snd_rawmidi_substream *substream)
1110 {
1111 	struct snd_ump_endpoint *ump = substream->rmidi->private_data;
1112 	int dir = substream->stream;
1113 	int group = ump->legacy_mapping[substream->number];
1114 
1115 	guard(mutex)(&ump->open_mutex);
1116 	scoped_guard(spinlock_irq, &ump->legacy_locks[dir])
1117 		ump->legacy_substreams[dir][group] = NULL;
1118 	if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
1119 		if (!--ump->legacy_out_opens)
1120 			snd_rawmidi_kernel_release(&ump->legacy_out_rfile);
1121 	}
1122 	return 0;
1123 }
1124 
snd_ump_legacy_trigger(struct snd_rawmidi_substream * substream,int up)1125 static void snd_ump_legacy_trigger(struct snd_rawmidi_substream *substream,
1126 				   int up)
1127 {
1128 	struct snd_ump_endpoint *ump = substream->rmidi->private_data;
1129 	int dir = substream->stream;
1130 
1131 	ump->ops->trigger(ump, dir, up);
1132 }
1133 
snd_ump_legacy_drain(struct snd_rawmidi_substream * substream)1134 static void snd_ump_legacy_drain(struct snd_rawmidi_substream *substream)
1135 {
1136 	struct snd_ump_endpoint *ump = substream->rmidi->private_data;
1137 
1138 	if (ump->ops->drain)
1139 		ump->ops->drain(ump, SNDRV_RAWMIDI_STREAM_OUTPUT);
1140 }
1141 
snd_ump_legacy_dev_register(struct snd_rawmidi * rmidi)1142 static int snd_ump_legacy_dev_register(struct snd_rawmidi *rmidi)
1143 {
1144 	/* dummy, just for avoiding create superfluous seq clients */
1145 	return 0;
1146 }
1147 
1148 static const struct snd_rawmidi_ops snd_ump_legacy_input_ops = {
1149 	.open = snd_ump_legacy_open,
1150 	.close = snd_ump_legacy_close,
1151 	.trigger = snd_ump_legacy_trigger,
1152 };
1153 
1154 static const struct snd_rawmidi_ops snd_ump_legacy_output_ops = {
1155 	.open = snd_ump_legacy_open,
1156 	.close = snd_ump_legacy_close,
1157 	.trigger = snd_ump_legacy_trigger,
1158 	.drain = snd_ump_legacy_drain,
1159 };
1160 
1161 static const struct snd_rawmidi_global_ops snd_ump_legacy_ops = {
1162 	.dev_register = snd_ump_legacy_dev_register,
1163 };
1164 
process_legacy_output(struct snd_ump_endpoint * ump,u32 * buffer,int count)1165 static int process_legacy_output(struct snd_ump_endpoint *ump,
1166 				 u32 *buffer, int count)
1167 {
1168 	struct snd_rawmidi_substream *substream;
1169 	struct ump_cvt_to_ump *ctx;
1170 	const int dir = SNDRV_RAWMIDI_STREAM_OUTPUT;
1171 	unsigned char c;
1172 	int group, size = 0;
1173 
1174 	if (!ump->out_cvts || !ump->legacy_out_opens)
1175 		return 0;
1176 
1177 	guard(spinlock_irqsave)(&ump->legacy_locks[dir]);
1178 	for (group = 0; group < SNDRV_UMP_MAX_GROUPS; group++) {
1179 		substream = ump->legacy_substreams[dir][group];
1180 		if (!substream)
1181 			continue;
1182 		ctx = &ump->out_cvts[group];
1183 		while (!ctx->ump_bytes &&
1184 		       snd_rawmidi_transmit(substream, &c, 1) > 0)
1185 			snd_ump_convert_to_ump(ctx, group, ump->info.protocol, c);
1186 		if (ctx->ump_bytes && ctx->ump_bytes <= count) {
1187 			size = ctx->ump_bytes;
1188 			memcpy(buffer, ctx->ump, size);
1189 			ctx->ump_bytes = 0;
1190 			break;
1191 		}
1192 	}
1193 	return size;
1194 }
1195 
process_legacy_input(struct snd_ump_endpoint * ump,const u32 * src,int words)1196 static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src,
1197 				 int words)
1198 {
1199 	struct snd_rawmidi_substream *substream;
1200 	unsigned char buf[16];
1201 	unsigned char group;
1202 	const int dir = SNDRV_RAWMIDI_STREAM_INPUT;
1203 	int size;
1204 
1205 	size = snd_ump_convert_from_ump(src, buf, &group);
1206 	if (size <= 0)
1207 		return;
1208 	guard(spinlock_irqsave)(&ump->legacy_locks[dir]);
1209 	substream = ump->legacy_substreams[dir][group];
1210 	if (substream)
1211 		snd_rawmidi_receive(substream, buf, size);
1212 }
1213 
1214 /* Fill ump->legacy_mapping[] for groups to be used for legacy rawmidi */
fill_legacy_mapping(struct snd_ump_endpoint * ump)1215 static int fill_legacy_mapping(struct snd_ump_endpoint *ump)
1216 {
1217 	struct snd_ump_block *fb;
1218 	unsigned int group_maps = 0;
1219 	int i, num;
1220 
1221 	if (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) {
1222 		list_for_each_entry(fb, &ump->block_list, list) {
1223 			for (i = 0; i < fb->info.num_groups; i++)
1224 				group_maps |= 1U << (fb->info.first_group + i);
1225 		}
1226 		if (!group_maps)
1227 			ump_info(ump, "No UMP Group is found in FB\n");
1228 	}
1229 
1230 	/* use all groups for non-static case */
1231 	if (!group_maps)
1232 		group_maps = (1U << SNDRV_UMP_MAX_GROUPS) - 1;
1233 
1234 	num = 0;
1235 	for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++)
1236 		if (group_maps & (1U << i))
1237 			ump->legacy_mapping[num++] = i;
1238 
1239 	return num;
1240 }
1241 
fill_substream_names(struct snd_ump_endpoint * ump,struct snd_rawmidi * rmidi,int dir)1242 static void fill_substream_names(struct snd_ump_endpoint *ump,
1243 				 struct snd_rawmidi *rmidi, int dir)
1244 {
1245 	struct snd_rawmidi_substream *s;
1246 	const char *name;
1247 	int idx;
1248 
1249 	list_for_each_entry(s, &rmidi->streams[dir].substreams, list) {
1250 		idx = ump->legacy_mapping[s->number];
1251 		name = ump->groups[idx].name;
1252 		if (!*name)
1253 			name = ump->info.name;
1254 		scnprintf(s->name, sizeof(s->name), "Group %d (%.16s)%s",
1255 			  idx + 1, name,
1256 			  ump->groups[idx].active ? "" : " [Inactive]");
1257 	}
1258 }
1259 
update_legacy_names(struct snd_ump_endpoint * ump)1260 static void update_legacy_names(struct snd_ump_endpoint *ump)
1261 {
1262 	struct snd_rawmidi *rmidi = ump->legacy_rmidi;
1263 
1264 	fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT);
1265 	fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT);
1266 }
1267 
snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint * ump,char * id,int device)1268 int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
1269 				  char *id, int device)
1270 {
1271 	struct snd_rawmidi *rmidi;
1272 	bool input, output;
1273 	int err, num;
1274 
1275 	ump->out_cvts = kcalloc(SNDRV_UMP_MAX_GROUPS,
1276 				sizeof(*ump->out_cvts), GFP_KERNEL);
1277 	if (!ump->out_cvts)
1278 		return -ENOMEM;
1279 
1280 	num = fill_legacy_mapping(ump);
1281 
1282 	input = ump->core.info_flags & SNDRV_RAWMIDI_INFO_INPUT;
1283 	output = ump->core.info_flags & SNDRV_RAWMIDI_INFO_OUTPUT;
1284 	err = snd_rawmidi_new(ump->core.card, id, device,
1285 			      output ? num : 0, input ? num : 0,
1286 			      &rmidi);
1287 	if (err < 0) {
1288 		kfree(ump->out_cvts);
1289 		return err;
1290 	}
1291 
1292 	if (input)
1293 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
1294 				    &snd_ump_legacy_input_ops);
1295 	if (output)
1296 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
1297 				    &snd_ump_legacy_output_ops);
1298 	snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)",
1299 		 ump->info.name);
1300 	rmidi->info_flags = ump->core.info_flags & ~SNDRV_RAWMIDI_INFO_UMP;
1301 	rmidi->ops = &snd_ump_legacy_ops;
1302 	rmidi->private_data = ump;
1303 	ump->legacy_rmidi = rmidi;
1304 	update_legacy_names(ump);
1305 
1306 	ump_dbg(ump, "Created a legacy rawmidi #%d (%s)\n", device, id);
1307 	return 0;
1308 }
1309 EXPORT_SYMBOL_GPL(snd_ump_attach_legacy_rawmidi);
1310 #endif /* CONFIG_SND_UMP_LEGACY_RAWMIDI */
1311 
1312 MODULE_DESCRIPTION("Universal MIDI Packet (UMP) Core Driver");
1313 MODULE_LICENSE("GPL");
1314