xref: /openbmc/linux/sound/core/ump.c (revision 9144f784f852f9a125cabe9927b986d909bfa439)
1e3a8a5b7STakashi Iwai // SPDX-License-Identifier: GPL-2.0-or-later
2e3a8a5b7STakashi Iwai /*
3e3a8a5b7STakashi Iwai  * Universal MIDI Packet (UMP) support
4e3a8a5b7STakashi Iwai  */
5e3a8a5b7STakashi Iwai 
6e3a8a5b7STakashi Iwai #include <linux/list.h>
7e3a8a5b7STakashi Iwai #include <linux/slab.h>
8e3a8a5b7STakashi Iwai #include <linux/module.h>
9e3a8a5b7STakashi Iwai #include <linux/export.h>
10e3a8a5b7STakashi Iwai #include <linux/mm.h>
11e3a8a5b7STakashi Iwai #include <sound/core.h>
12e3a8a5b7STakashi Iwai #include <sound/rawmidi.h>
13e3a8a5b7STakashi Iwai #include <sound/ump.h>
1433cd7630STakashi Iwai #include <sound/ump_convert.h>
15e3a8a5b7STakashi Iwai 
16ea29a02fSTakashi Iwai #define ump_err(ump, fmt, args...)	dev_err((ump)->core.dev, fmt, ##args)
17ea29a02fSTakashi Iwai #define ump_warn(ump, fmt, args...)	dev_warn((ump)->core.dev, fmt, ##args)
18ea29a02fSTakashi Iwai #define ump_info(ump, fmt, args...)	dev_info((ump)->core.dev, fmt, ##args)
19ea29a02fSTakashi Iwai #define ump_dbg(ump, fmt, args...)	dev_dbg((ump)->core.dev, fmt, ##args)
20e3a8a5b7STakashi Iwai 
21e3a8a5b7STakashi Iwai static int snd_ump_dev_register(struct snd_rawmidi *rmidi);
22e3a8a5b7STakashi Iwai static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi);
23e3a8a5b7STakashi Iwai static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd,
24e3a8a5b7STakashi Iwai 			  void __user *argp);
25fa030f66STakashi Iwai static void snd_ump_proc_read(struct snd_info_entry *entry,
26fa030f66STakashi Iwai 			      struct snd_info_buffer *buffer);
276b41e64aSTakashi Iwai static int snd_ump_rawmidi_open(struct snd_rawmidi_substream *substream);
286b41e64aSTakashi Iwai static int snd_ump_rawmidi_close(struct snd_rawmidi_substream *substream);
296b41e64aSTakashi Iwai static void snd_ump_rawmidi_trigger(struct snd_rawmidi_substream *substream,
306b41e64aSTakashi Iwai 				    int up);
316b41e64aSTakashi Iwai static void snd_ump_rawmidi_drain(struct snd_rawmidi_substream *substream);
32e3a8a5b7STakashi Iwai 
3337e0e141STakashi Iwai static void ump_handle_stream_msg(struct snd_ump_endpoint *ump,
3437e0e141STakashi Iwai 				  const u32 *buf, int size);
350b5288f5STakashi Iwai #if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
360b5288f5STakashi Iwai static int process_legacy_output(struct snd_ump_endpoint *ump,
370b5288f5STakashi Iwai 				 u32 *buffer, int count);
380b5288f5STakashi Iwai static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src,
390b5288f5STakashi Iwai 				 int words);
408d891c86STakashi Iwai static void update_legacy_names(struct snd_ump_endpoint *ump);
410b5288f5STakashi Iwai #else
process_legacy_output(struct snd_ump_endpoint * ump,u32 * buffer,int count)420b5288f5STakashi Iwai static inline int process_legacy_output(struct snd_ump_endpoint *ump,
430b5288f5STakashi Iwai 					u32 *buffer, int count)
440b5288f5STakashi Iwai {
450b5288f5STakashi Iwai 	return 0;
460b5288f5STakashi Iwai }
process_legacy_input(struct snd_ump_endpoint * ump,const u32 * src,int words)470b5288f5STakashi Iwai static inline void process_legacy_input(struct snd_ump_endpoint *ump,
480b5288f5STakashi Iwai 					const u32 *src, int words)
490b5288f5STakashi Iwai {
500b5288f5STakashi Iwai }
update_legacy_names(struct snd_ump_endpoint * ump)518d891c86STakashi Iwai static inline void update_legacy_names(struct snd_ump_endpoint *ump)
528d891c86STakashi Iwai {
538d891c86STakashi Iwai }
540b5288f5STakashi Iwai #endif
550b5288f5STakashi Iwai 
56e3a8a5b7STakashi Iwai static const struct snd_rawmidi_global_ops snd_ump_rawmidi_ops = {
57e3a8a5b7STakashi Iwai 	.dev_register = snd_ump_dev_register,
58e3a8a5b7STakashi Iwai 	.dev_unregister = snd_ump_dev_unregister,
59e3a8a5b7STakashi Iwai 	.ioctl = snd_ump_ioctl,
60fa030f66STakashi Iwai 	.proc_read = snd_ump_proc_read,
61e3a8a5b7STakashi Iwai };
62e3a8a5b7STakashi Iwai 
636b41e64aSTakashi Iwai static const struct snd_rawmidi_ops snd_ump_rawmidi_input_ops = {
646b41e64aSTakashi Iwai 	.open = snd_ump_rawmidi_open,
656b41e64aSTakashi Iwai 	.close = snd_ump_rawmidi_close,
666b41e64aSTakashi Iwai 	.trigger = snd_ump_rawmidi_trigger,
676b41e64aSTakashi Iwai };
686b41e64aSTakashi Iwai 
696b41e64aSTakashi Iwai static const struct snd_rawmidi_ops snd_ump_rawmidi_output_ops = {
706b41e64aSTakashi Iwai 	.open = snd_ump_rawmidi_open,
716b41e64aSTakashi Iwai 	.close = snd_ump_rawmidi_close,
726b41e64aSTakashi Iwai 	.trigger = snd_ump_rawmidi_trigger,
736b41e64aSTakashi Iwai 	.drain = snd_ump_rawmidi_drain,
746b41e64aSTakashi Iwai };
756b41e64aSTakashi Iwai 
snd_ump_endpoint_free(struct snd_rawmidi * rmidi)76e3a8a5b7STakashi Iwai static void snd_ump_endpoint_free(struct snd_rawmidi *rmidi)
77e3a8a5b7STakashi Iwai {
78e3a8a5b7STakashi Iwai 	struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
79e3a8a5b7STakashi Iwai 	struct snd_ump_block *fb;
80e3a8a5b7STakashi Iwai 
81e3a8a5b7STakashi Iwai 	while (!list_empty(&ump->block_list)) {
82e3a8a5b7STakashi Iwai 		fb = list_first_entry(&ump->block_list, struct snd_ump_block,
83e3a8a5b7STakashi Iwai 				      list);
84e3a8a5b7STakashi Iwai 		list_del(&fb->list);
85e3a8a5b7STakashi Iwai 		if (fb->private_free)
86e3a8a5b7STakashi Iwai 			fb->private_free(fb);
87e3a8a5b7STakashi Iwai 		kfree(fb);
88e3a8a5b7STakashi Iwai 	}
89e3a8a5b7STakashi Iwai 
90e3a8a5b7STakashi Iwai 	if (ump->private_free)
91e3a8a5b7STakashi Iwai 		ump->private_free(ump);
920b5288f5STakashi Iwai 
930b5288f5STakashi Iwai #if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
9433cd7630STakashi Iwai 	kfree(ump->out_cvts);
950b5288f5STakashi Iwai #endif
96e3a8a5b7STakashi Iwai }
97e3a8a5b7STakashi Iwai 
98e3a8a5b7STakashi Iwai /**
99e3a8a5b7STakashi Iwai  * snd_ump_endpoint_new - create a UMP Endpoint object
100e3a8a5b7STakashi Iwai  * @card: the card instance
101e3a8a5b7STakashi Iwai  * @id: the id string for rawmidi
102e3a8a5b7STakashi Iwai  * @device: the device index for rawmidi
103e3a8a5b7STakashi Iwai  * @output: 1 for enabling output
104e3a8a5b7STakashi Iwai  * @input: 1 for enabling input
105e3a8a5b7STakashi Iwai  * @ump_ret: the pointer to store the new UMP instance
106e3a8a5b7STakashi Iwai  *
107e3a8a5b7STakashi Iwai  * Creates a new UMP Endpoint object. A UMP Endpoint is tied with one rawmidi
108e3a8a5b7STakashi Iwai  * instance with one input and/or one output rawmidi stream (either uni-
109e3a8a5b7STakashi Iwai  * or bi-directional). A UMP Endpoint may contain one or multiple UMP Blocks
110e3a8a5b7STakashi Iwai  * that consist of one or multiple UMP Groups.
111e3a8a5b7STakashi Iwai  *
112e3a8a5b7STakashi Iwai  * Use snd_rawmidi_set_ops() to set the operators to the new instance.
113e3a8a5b7STakashi Iwai  * Unlike snd_rawmidi_new(), this function sets up the info_flags by itself
114e3a8a5b7STakashi Iwai  * depending on the given @output and @input.
115e3a8a5b7STakashi Iwai  *
116e3a8a5b7STakashi Iwai  * The device has SNDRV_RAWMIDI_INFO_UMP flag set and a different device
117e3a8a5b7STakashi Iwai  * file ("umpCxDx") than a standard MIDI 1.x device ("midiCxDx") is
118e3a8a5b7STakashi Iwai  * created.
119e3a8a5b7STakashi Iwai  *
120e3a8a5b7STakashi Iwai  * Return: Zero if successful, or a negative error code on failure.
121e3a8a5b7STakashi Iwai  */
snd_ump_endpoint_new(struct snd_card * card,char * id,int device,int output,int input,struct snd_ump_endpoint ** ump_ret)122e3a8a5b7STakashi Iwai int snd_ump_endpoint_new(struct snd_card *card, char *id, int device,
123e3a8a5b7STakashi Iwai 			 int output, int input,
124e3a8a5b7STakashi Iwai 			 struct snd_ump_endpoint **ump_ret)
125e3a8a5b7STakashi Iwai {
126e3a8a5b7STakashi Iwai 	unsigned int info_flags = SNDRV_RAWMIDI_INFO_UMP;
127e3a8a5b7STakashi Iwai 	struct snd_ump_endpoint *ump;
128e3a8a5b7STakashi Iwai 	int err;
129e3a8a5b7STakashi Iwai 
130e3a8a5b7STakashi Iwai 	if (input)
131e3a8a5b7STakashi Iwai 		info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
132e3a8a5b7STakashi Iwai 	if (output)
133e3a8a5b7STakashi Iwai 		info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
134e3a8a5b7STakashi Iwai 	if (input && output)
135e3a8a5b7STakashi Iwai 		info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
136e3a8a5b7STakashi Iwai 
137e3a8a5b7STakashi Iwai 	ump = kzalloc(sizeof(*ump), GFP_KERNEL);
138e3a8a5b7STakashi Iwai 	if (!ump)
139e3a8a5b7STakashi Iwai 		return -ENOMEM;
140e3a8a5b7STakashi Iwai 	INIT_LIST_HEAD(&ump->block_list);
1410b5288f5STakashi Iwai 	mutex_init(&ump->open_mutex);
14237e0e141STakashi Iwai 	init_waitqueue_head(&ump->stream_wait);
14381fd444aSTakashi Iwai #if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
1440b5288f5STakashi Iwai 	spin_lock_init(&ump->legacy_locks[0]);
1450b5288f5STakashi Iwai 	spin_lock_init(&ump->legacy_locks[1]);
1460b5288f5STakashi Iwai #endif
147e3a8a5b7STakashi Iwai 	err = snd_rawmidi_init(&ump->core, card, id, device,
148e3a8a5b7STakashi Iwai 			       output, input, info_flags);
149e3a8a5b7STakashi Iwai 	if (err < 0) {
150e3a8a5b7STakashi Iwai 		snd_rawmidi_free(&ump->core);
151e3a8a5b7STakashi Iwai 		return err;
152e3a8a5b7STakashi Iwai 	}
153e3a8a5b7STakashi Iwai 
154e3a8a5b7STakashi Iwai 	ump->info.card = card->number;
155e3a8a5b7STakashi Iwai 	ump->info.device = device;
156e3a8a5b7STakashi Iwai 
157e3a8a5b7STakashi Iwai 	ump->core.private_free = snd_ump_endpoint_free;
158e3a8a5b7STakashi Iwai 	ump->core.ops = &snd_ump_rawmidi_ops;
1596b41e64aSTakashi Iwai 	if (input)
1606b41e64aSTakashi Iwai 		snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_INPUT,
1616b41e64aSTakashi Iwai 				    &snd_ump_rawmidi_input_ops);
1626b41e64aSTakashi Iwai 	if (output)
1636b41e64aSTakashi Iwai 		snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_OUTPUT,
1646b41e64aSTakashi Iwai 				    &snd_ump_rawmidi_output_ops);
165e3a8a5b7STakashi Iwai 
166e3a8a5b7STakashi Iwai 	ump_dbg(ump, "Created a UMP EP #%d (%s)\n", device, id);
167e3a8a5b7STakashi Iwai 	*ump_ret = ump;
168e3a8a5b7STakashi Iwai 	return 0;
169e3a8a5b7STakashi Iwai }
170e3a8a5b7STakashi Iwai EXPORT_SYMBOL_GPL(snd_ump_endpoint_new);
171e3a8a5b7STakashi Iwai 
172e3a8a5b7STakashi Iwai /*
173e3a8a5b7STakashi Iwai  * Device register / unregister hooks;
174e3a8a5b7STakashi Iwai  *  do nothing, placeholders for avoiding the default rawmidi handling
175e3a8a5b7STakashi Iwai  */
17681fd444aSTakashi Iwai 
17781fd444aSTakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQUENCER)
snd_ump_dev_seq_free(struct snd_seq_device * device)17881fd444aSTakashi Iwai static void snd_ump_dev_seq_free(struct snd_seq_device *device)
17981fd444aSTakashi Iwai {
18081fd444aSTakashi Iwai 	struct snd_ump_endpoint *ump = device->private_data;
18181fd444aSTakashi Iwai 
18281fd444aSTakashi Iwai 	ump->seq_dev = NULL;
18381fd444aSTakashi Iwai }
18481fd444aSTakashi Iwai #endif
18581fd444aSTakashi Iwai 
snd_ump_dev_register(struct snd_rawmidi * rmidi)186e3a8a5b7STakashi Iwai static int snd_ump_dev_register(struct snd_rawmidi *rmidi)
187e3a8a5b7STakashi Iwai {
18881fd444aSTakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQUENCER)
18981fd444aSTakashi Iwai 	struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
19081fd444aSTakashi Iwai 	int err;
19181fd444aSTakashi Iwai 
19281fd444aSTakashi Iwai 	err = snd_seq_device_new(ump->core.card, ump->core.device,
19381fd444aSTakashi Iwai 				 SNDRV_SEQ_DEV_ID_UMP, 0, &ump->seq_dev);
19481fd444aSTakashi Iwai 	if (err < 0)
19581fd444aSTakashi Iwai 		return err;
19681fd444aSTakashi Iwai 	ump->seq_dev->private_data = ump;
19781fd444aSTakashi Iwai 	ump->seq_dev->private_free = snd_ump_dev_seq_free;
19881fd444aSTakashi Iwai 	snd_device_register(ump->core.card, ump->seq_dev);
19981fd444aSTakashi Iwai #endif
200e3a8a5b7STakashi Iwai 	return 0;
201e3a8a5b7STakashi Iwai }
202e3a8a5b7STakashi Iwai 
snd_ump_dev_unregister(struct snd_rawmidi * rmidi)203e3a8a5b7STakashi Iwai static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi)
204e3a8a5b7STakashi Iwai {
205e3a8a5b7STakashi Iwai 	return 0;
206e3a8a5b7STakashi Iwai }
207e3a8a5b7STakashi Iwai 
208e3a8a5b7STakashi Iwai static struct snd_ump_block *
snd_ump_get_block(struct snd_ump_endpoint * ump,unsigned char id)209e3a8a5b7STakashi Iwai snd_ump_get_block(struct snd_ump_endpoint *ump, unsigned char id)
210e3a8a5b7STakashi Iwai {
211e3a8a5b7STakashi Iwai 	struct snd_ump_block *fb;
212e3a8a5b7STakashi Iwai 
213e3a8a5b7STakashi Iwai 	list_for_each_entry(fb, &ump->block_list, list) {
214e3a8a5b7STakashi Iwai 		if (fb->info.block_id == id)
215e3a8a5b7STakashi Iwai 			return fb;
216e3a8a5b7STakashi Iwai 	}
217e3a8a5b7STakashi Iwai 	return NULL;
218e3a8a5b7STakashi Iwai }
219e3a8a5b7STakashi Iwai 
2206b41e64aSTakashi Iwai /*
2216b41e64aSTakashi Iwai  * rawmidi ops for UMP endpoint
2226b41e64aSTakashi Iwai  */
snd_ump_rawmidi_open(struct snd_rawmidi_substream * substream)2236b41e64aSTakashi Iwai static int snd_ump_rawmidi_open(struct snd_rawmidi_substream *substream)
2246b41e64aSTakashi Iwai {
2256b41e64aSTakashi Iwai 	struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
2266b41e64aSTakashi Iwai 	int dir = substream->stream;
2276b41e64aSTakashi Iwai 	int err;
2286b41e64aSTakashi Iwai 
2296b41e64aSTakashi Iwai 	if (ump->substreams[dir])
2306b41e64aSTakashi Iwai 		return -EBUSY;
2316b41e64aSTakashi Iwai 	err = ump->ops->open(ump, dir);
2326b41e64aSTakashi Iwai 	if (err < 0)
2336b41e64aSTakashi Iwai 		return err;
2346b41e64aSTakashi Iwai 	ump->substreams[dir] = substream;
2356b41e64aSTakashi Iwai 	return 0;
2366b41e64aSTakashi Iwai }
2376b41e64aSTakashi Iwai 
snd_ump_rawmidi_close(struct snd_rawmidi_substream * substream)2386b41e64aSTakashi Iwai static int snd_ump_rawmidi_close(struct snd_rawmidi_substream *substream)
2396b41e64aSTakashi Iwai {
2406b41e64aSTakashi Iwai 	struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
2416b41e64aSTakashi Iwai 	int dir = substream->stream;
2426b41e64aSTakashi Iwai 
2436b41e64aSTakashi Iwai 	ump->substreams[dir] = NULL;
2446b41e64aSTakashi Iwai 	ump->ops->close(ump, dir);
2456b41e64aSTakashi Iwai 	return 0;
2466b41e64aSTakashi Iwai }
2476b41e64aSTakashi Iwai 
snd_ump_rawmidi_trigger(struct snd_rawmidi_substream * substream,int up)2486b41e64aSTakashi Iwai static void snd_ump_rawmidi_trigger(struct snd_rawmidi_substream *substream,
2496b41e64aSTakashi Iwai 				    int up)
2506b41e64aSTakashi Iwai {
2516b41e64aSTakashi Iwai 	struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
2526b41e64aSTakashi Iwai 	int dir = substream->stream;
2536b41e64aSTakashi Iwai 
2546b41e64aSTakashi Iwai 	ump->ops->trigger(ump, dir, up);
2556b41e64aSTakashi Iwai }
2566b41e64aSTakashi Iwai 
snd_ump_rawmidi_drain(struct snd_rawmidi_substream * substream)2576b41e64aSTakashi Iwai static void snd_ump_rawmidi_drain(struct snd_rawmidi_substream *substream)
2586b41e64aSTakashi Iwai {
2596b41e64aSTakashi Iwai 	struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
2606b41e64aSTakashi Iwai 
2616b41e64aSTakashi Iwai 	if (ump->ops->drain)
2626b41e64aSTakashi Iwai 		ump->ops->drain(ump, SNDRV_RAWMIDI_STREAM_OUTPUT);
2636b41e64aSTakashi Iwai }
2646b41e64aSTakashi Iwai 
2650b5288f5STakashi Iwai /* number of 32bit words per message type */
2660b5288f5STakashi Iwai static unsigned char ump_packet_words[0x10] = {
2670b5288f5STakashi Iwai 	1, 1, 1, 2, 2, 4, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4
2680b5288f5STakashi Iwai };
2690b5288f5STakashi Iwai 
2704dce2f07STakashi Iwai /**
2714dce2f07STakashi Iwai  * snd_ump_receive_ump_val - parse the UMP packet data
2724dce2f07STakashi Iwai  * @ump: UMP endpoint
2734dce2f07STakashi Iwai  * @val: UMP packet data
2744dce2f07STakashi Iwai  *
2754dce2f07STakashi Iwai  * The data is copied onto ump->input_buf[].
2760b5288f5STakashi Iwai  * When a full packet is completed, returns the number of words (from 1 to 4).
2770b5288f5STakashi Iwai  * OTOH, if the packet is incomplete, returns 0.
2780b5288f5STakashi Iwai  */
snd_ump_receive_ump_val(struct snd_ump_endpoint * ump,u32 val)2794dce2f07STakashi Iwai int snd_ump_receive_ump_val(struct snd_ump_endpoint *ump, u32 val)
2800b5288f5STakashi Iwai {
2810b5288f5STakashi Iwai 	int words;
2820b5288f5STakashi Iwai 
2830b5288f5STakashi Iwai 	if (!ump->input_pending)
2840b5288f5STakashi Iwai 		ump->input_pending = ump_packet_words[ump_message_type(val)];
2850b5288f5STakashi Iwai 
2860b5288f5STakashi Iwai 	ump->input_buf[ump->input_buf_head++] = val;
2870b5288f5STakashi Iwai 	ump->input_pending--;
2880b5288f5STakashi Iwai 	if (!ump->input_pending) {
2890b5288f5STakashi Iwai 		words = ump->input_buf_head;
2900b5288f5STakashi Iwai 		ump->input_buf_head = 0;
2910b5288f5STakashi Iwai 		return words;
2920b5288f5STakashi Iwai 	}
2930b5288f5STakashi Iwai 	return 0;
2940b5288f5STakashi Iwai }
2954dce2f07STakashi Iwai EXPORT_SYMBOL_GPL(snd_ump_receive_ump_val);
2960b5288f5STakashi Iwai 
2976b41e64aSTakashi Iwai /**
2986b41e64aSTakashi Iwai  * snd_ump_receive - transfer UMP packets from the device
2996b41e64aSTakashi Iwai  * @ump: the UMP endpoint
3006b41e64aSTakashi Iwai  * @buffer: the buffer pointer to transfer
3016b41e64aSTakashi Iwai  * @count: byte size to transfer
3026b41e64aSTakashi Iwai  *
3036b41e64aSTakashi Iwai  * Called from the driver to submit the received UMP packets from the device
3046b41e64aSTakashi Iwai  * to user-space.  It's essentially a wrapper of rawmidi_receive().
3056b41e64aSTakashi Iwai  * The data to receive is in CPU-native endianness.
3066b41e64aSTakashi Iwai  */
snd_ump_receive(struct snd_ump_endpoint * ump,const u32 * buffer,int count)3076b41e64aSTakashi Iwai int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count)
3086b41e64aSTakashi Iwai {
3090b5288f5STakashi Iwai 	struct snd_rawmidi_substream *substream;
3100b5288f5STakashi Iwai 	const u32 *p = buffer;
3110b5288f5STakashi Iwai 	int n, words = count >> 2;
3126b41e64aSTakashi Iwai 
3130b5288f5STakashi Iwai 	while (words--) {
3140b5288f5STakashi Iwai 		n = snd_ump_receive_ump_val(ump, *p++);
3150b5288f5STakashi Iwai 		if (!n)
3160b5288f5STakashi Iwai 			continue;
31737e0e141STakashi Iwai 		ump_handle_stream_msg(ump, ump->input_buf, n);
31881fd444aSTakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQUENCER)
31981fd444aSTakashi Iwai 		if (ump->seq_ops)
32081fd444aSTakashi Iwai 			ump->seq_ops->input_receive(ump, ump->input_buf, n);
32181fd444aSTakashi Iwai #endif
3220b5288f5STakashi Iwai 		process_legacy_input(ump, ump->input_buf, n);
3230b5288f5STakashi Iwai 	}
3240b5288f5STakashi Iwai 
3250b5288f5STakashi Iwai 	substream = ump->substreams[SNDRV_RAWMIDI_STREAM_INPUT];
3266b41e64aSTakashi Iwai 	if (!substream)
3276b41e64aSTakashi Iwai 		return 0;
3286b41e64aSTakashi Iwai 	return snd_rawmidi_receive(substream, (const char *)buffer, count);
3296b41e64aSTakashi Iwai }
3306b41e64aSTakashi Iwai EXPORT_SYMBOL_GPL(snd_ump_receive);
3316b41e64aSTakashi Iwai 
3326b41e64aSTakashi Iwai /**
3336b41e64aSTakashi Iwai  * snd_ump_transmit - transmit UMP packets
3346b41e64aSTakashi Iwai  * @ump: the UMP endpoint
3356b41e64aSTakashi Iwai  * @buffer: the buffer pointer to transfer
3366b41e64aSTakashi Iwai  * @count: byte size to transfer
3376b41e64aSTakashi Iwai  *
3386b41e64aSTakashi Iwai  * Called from the driver to obtain the UMP packets from user-space to the
3396b41e64aSTakashi Iwai  * device.  It's essentially a wrapper of rawmidi_transmit().
3406b41e64aSTakashi Iwai  * The data to transmit is in CPU-native endianness.
3416b41e64aSTakashi Iwai  */
snd_ump_transmit(struct snd_ump_endpoint * ump,u32 * buffer,int count)3426b41e64aSTakashi Iwai int snd_ump_transmit(struct snd_ump_endpoint *ump, u32 *buffer, int count)
3436b41e64aSTakashi Iwai {
3446b41e64aSTakashi Iwai 	struct snd_rawmidi_substream *substream =
3456b41e64aSTakashi Iwai 		ump->substreams[SNDRV_RAWMIDI_STREAM_OUTPUT];
3460b5288f5STakashi Iwai 	int err;
3476b41e64aSTakashi Iwai 
3486b41e64aSTakashi Iwai 	if (!substream)
3496b41e64aSTakashi Iwai 		return -ENODEV;
3500b5288f5STakashi Iwai 	err = snd_rawmidi_transmit(substream, (char *)buffer, count);
3510b5288f5STakashi Iwai 	/* received either data or an error? */
3520b5288f5STakashi Iwai 	if (err)
3530b5288f5STakashi Iwai 		return err;
3540b5288f5STakashi Iwai 	return process_legacy_output(ump, buffer, count);
3556b41e64aSTakashi Iwai }
3566b41e64aSTakashi Iwai EXPORT_SYMBOL_GPL(snd_ump_transmit);
3576b41e64aSTakashi Iwai 
358e3a8a5b7STakashi Iwai /**
359e3a8a5b7STakashi Iwai  * snd_ump_block_new - Create a UMP block
360e3a8a5b7STakashi Iwai  * @ump: UMP object
361e3a8a5b7STakashi Iwai  * @blk: block ID number to create
362e3a8a5b7STakashi Iwai  * @direction: direction (in/out/bidirection)
363e3a8a5b7STakashi Iwai  * @first_group: the first group ID (0-based)
364e3a8a5b7STakashi Iwai  * @num_groups: the number of groups in this block
365e3a8a5b7STakashi Iwai  * @blk_ret: the pointer to store the resultant block object
366e3a8a5b7STakashi Iwai  */
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)367e3a8a5b7STakashi Iwai int snd_ump_block_new(struct snd_ump_endpoint *ump, unsigned int blk,
368e3a8a5b7STakashi Iwai 		      unsigned int direction, unsigned int first_group,
369e3a8a5b7STakashi Iwai 		      unsigned int num_groups, struct snd_ump_block **blk_ret)
370e3a8a5b7STakashi Iwai {
371e3a8a5b7STakashi Iwai 	struct snd_ump_block *fb, *p;
372e3a8a5b7STakashi Iwai 
373e3a8a5b7STakashi Iwai 	if (blk < 0 || blk >= SNDRV_UMP_MAX_BLOCKS)
374e3a8a5b7STakashi Iwai 		return -EINVAL;
375e3a8a5b7STakashi Iwai 
376e3a8a5b7STakashi Iwai 	if (snd_ump_get_block(ump, blk))
377e3a8a5b7STakashi Iwai 		return -EBUSY;
378e3a8a5b7STakashi Iwai 
379e3a8a5b7STakashi Iwai 	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
380e3a8a5b7STakashi Iwai 	if (!fb)
381e3a8a5b7STakashi Iwai 		return -ENOMEM;
382e3a8a5b7STakashi Iwai 
383e3a8a5b7STakashi Iwai 	fb->ump = ump;
384e3a8a5b7STakashi Iwai 	fb->info.card = ump->info.card;
385e3a8a5b7STakashi Iwai 	fb->info.device = ump->info.device;
386e3a8a5b7STakashi Iwai 	fb->info.block_id = blk;
387e3a8a5b7STakashi Iwai 	if (blk >= ump->info.num_blocks)
388e3a8a5b7STakashi Iwai 		ump->info.num_blocks = blk + 1;
389e3a8a5b7STakashi Iwai 	fb->info.direction = direction;
390e3a8a5b7STakashi Iwai 	fb->info.active = 1;
391e3a8a5b7STakashi Iwai 	fb->info.first_group = first_group;
392e3a8a5b7STakashi Iwai 	fb->info.num_groups = num_groups;
393e3a8a5b7STakashi Iwai 	/* fill the default name, may be overwritten to a better name */
394e3a8a5b7STakashi Iwai 	snprintf(fb->info.name, sizeof(fb->info.name), "Group %d-%d",
395e3a8a5b7STakashi Iwai 		 first_group + 1, first_group + num_groups);
396e3a8a5b7STakashi Iwai 
397e3a8a5b7STakashi Iwai 	/* put the entry in the ordered list */
398e3a8a5b7STakashi Iwai 	list_for_each_entry(p, &ump->block_list, list) {
399e3a8a5b7STakashi Iwai 		if (p->info.block_id > blk) {
400e3a8a5b7STakashi Iwai 			list_add_tail(&fb->list, &p->list);
401e3a8a5b7STakashi Iwai 			goto added;
402e3a8a5b7STakashi Iwai 		}
403e3a8a5b7STakashi Iwai 	}
404e3a8a5b7STakashi Iwai 	list_add_tail(&fb->list, &ump->block_list);
405e3a8a5b7STakashi Iwai 
406e3a8a5b7STakashi Iwai  added:
407e3a8a5b7STakashi Iwai 	ump_dbg(ump, "Created a UMP Block #%d (%s)\n", blk, fb->info.name);
408e3a8a5b7STakashi Iwai 	*blk_ret = fb;
409e3a8a5b7STakashi Iwai 	return 0;
410e3a8a5b7STakashi Iwai }
411e3a8a5b7STakashi Iwai EXPORT_SYMBOL_GPL(snd_ump_block_new);
412e3a8a5b7STakashi Iwai 
snd_ump_ioctl_block(struct snd_ump_endpoint * ump,struct snd_ump_block_info __user * argp)413e3a8a5b7STakashi Iwai static int snd_ump_ioctl_block(struct snd_ump_endpoint *ump,
414e3a8a5b7STakashi Iwai 			       struct snd_ump_block_info __user *argp)
415e3a8a5b7STakashi Iwai {
416e3a8a5b7STakashi Iwai 	struct snd_ump_block *fb;
417e3a8a5b7STakashi Iwai 	unsigned char id;
418e3a8a5b7STakashi Iwai 
419e3a8a5b7STakashi Iwai 	if (get_user(id, &argp->block_id))
420e3a8a5b7STakashi Iwai 		return -EFAULT;
421e3a8a5b7STakashi Iwai 	fb = snd_ump_get_block(ump, id);
422e3a8a5b7STakashi Iwai 	if (!fb)
423e3a8a5b7STakashi Iwai 		return -ENOENT;
424e3a8a5b7STakashi Iwai 	if (copy_to_user(argp, &fb->info, sizeof(fb->info)))
425e3a8a5b7STakashi Iwai 		return -EFAULT;
426e3a8a5b7STakashi Iwai 	return 0;
427e3a8a5b7STakashi Iwai }
428e3a8a5b7STakashi Iwai 
429e3a8a5b7STakashi Iwai /*
430e3a8a5b7STakashi Iwai  * Handle UMP-specific ioctls; called from snd_rawmidi_ioctl()
431e3a8a5b7STakashi Iwai  */
snd_ump_ioctl(struct snd_rawmidi * rmidi,unsigned int cmd,void __user * argp)432e3a8a5b7STakashi Iwai static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd,
433e3a8a5b7STakashi Iwai 			  void __user *argp)
434e3a8a5b7STakashi Iwai {
435e3a8a5b7STakashi Iwai 	struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
436e3a8a5b7STakashi Iwai 
437e3a8a5b7STakashi Iwai 	switch (cmd) {
438e3a8a5b7STakashi Iwai 	case SNDRV_UMP_IOCTL_ENDPOINT_INFO:
439e3a8a5b7STakashi Iwai 		if (copy_to_user(argp, &ump->info, sizeof(ump->info)))
440e3a8a5b7STakashi Iwai 			return -EFAULT;
441e3a8a5b7STakashi Iwai 		return 0;
442e3a8a5b7STakashi Iwai 	case SNDRV_UMP_IOCTL_BLOCK_INFO:
443e3a8a5b7STakashi Iwai 		return snd_ump_ioctl_block(ump, argp);
444e3a8a5b7STakashi Iwai 	default:
445e3a8a5b7STakashi Iwai 		ump_dbg(ump, "rawmidi: unknown command = 0x%x\n", cmd);
446e3a8a5b7STakashi Iwai 		return -ENOTTY;
447e3a8a5b7STakashi Iwai 	}
448e3a8a5b7STakashi Iwai }
449e3a8a5b7STakashi Iwai 
ump_direction_string(int dir)450fa030f66STakashi Iwai static const char *ump_direction_string(int dir)
451fa030f66STakashi Iwai {
452fa030f66STakashi Iwai 	switch (dir) {
453fa030f66STakashi Iwai 	case SNDRV_UMP_DIR_INPUT:
454fa030f66STakashi Iwai 		return "input";
455fa030f66STakashi Iwai 	case SNDRV_UMP_DIR_OUTPUT:
456fa030f66STakashi Iwai 		return "output";
457fa030f66STakashi Iwai 	case SNDRV_UMP_DIR_BIDIRECTION:
458fa030f66STakashi Iwai 		return "bidirection";
459fa030f66STakashi Iwai 	default:
460fa030f66STakashi Iwai 		return "unknown";
461fa030f66STakashi Iwai 	}
462fa030f66STakashi Iwai }
463fa030f66STakashi Iwai 
ump_ui_hint_string(int dir)464e375b8a0STakashi Iwai static const char *ump_ui_hint_string(int dir)
465e375b8a0STakashi Iwai {
466e375b8a0STakashi Iwai 	switch (dir) {
467e375b8a0STakashi Iwai 	case  SNDRV_UMP_BLOCK_UI_HINT_RECEIVER:
468e375b8a0STakashi Iwai 		return "receiver";
469e375b8a0STakashi Iwai 	case SNDRV_UMP_BLOCK_UI_HINT_SENDER:
470e375b8a0STakashi Iwai 		return "sender";
471e375b8a0STakashi Iwai 	case SNDRV_UMP_BLOCK_UI_HINT_BOTH:
472e375b8a0STakashi Iwai 		return "both";
473e375b8a0STakashi Iwai 	default:
474e375b8a0STakashi Iwai 		return "unknown";
475e375b8a0STakashi Iwai 	}
476e375b8a0STakashi Iwai }
477e375b8a0STakashi Iwai 
478fa030f66STakashi Iwai /* Additional proc file output */
snd_ump_proc_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)479fa030f66STakashi Iwai static void snd_ump_proc_read(struct snd_info_entry *entry,
480fa030f66STakashi Iwai 			      struct snd_info_buffer *buffer)
481fa030f66STakashi Iwai {
482fa030f66STakashi Iwai 	struct snd_rawmidi *rmidi = entry->private_data;
483fa030f66STakashi Iwai 	struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
484fa030f66STakashi Iwai 	struct snd_ump_block *fb;
485fa030f66STakashi Iwai 
486fa030f66STakashi Iwai 	snd_iprintf(buffer, "EP Name: %s\n", ump->info.name);
487fa030f66STakashi Iwai 	snd_iprintf(buffer, "EP Product ID: %s\n", ump->info.product_id);
488fa030f66STakashi Iwai 	snd_iprintf(buffer, "UMP Version: 0x%04x\n", ump->info.version);
489fa030f66STakashi Iwai 	snd_iprintf(buffer, "Protocol Caps: 0x%08x\n", ump->info.protocol_caps);
490fa030f66STakashi Iwai 	snd_iprintf(buffer, "Protocol: 0x%08x\n", ump->info.protocol);
491e375b8a0STakashi Iwai 	if (ump->info.version) {
492e375b8a0STakashi Iwai 		snd_iprintf(buffer, "Manufacturer ID: 0x%08x\n",
493e375b8a0STakashi Iwai 			    ump->info.manufacturer_id);
494e375b8a0STakashi Iwai 		snd_iprintf(buffer, "Family ID: 0x%04x\n", ump->info.family_id);
495e375b8a0STakashi Iwai 		snd_iprintf(buffer, "Model ID: 0x%04x\n", ump->info.model_id);
496e375b8a0STakashi Iwai 		snd_iprintf(buffer, "SW Revision: 0x%02x%02x%02x%02x\n",
497e375b8a0STakashi Iwai 			    ump->info.sw_revision[0],
498e375b8a0STakashi Iwai 			    ump->info.sw_revision[1],
499e375b8a0STakashi Iwai 			    ump->info.sw_revision[2],
500e375b8a0STakashi Iwai 			    ump->info.sw_revision[3]);
501e375b8a0STakashi Iwai 	}
50201dfa8e9STakashi Iwai 	snd_iprintf(buffer, "Static Blocks: %s\n",
50301dfa8e9STakashi Iwai 		    (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) ? "Yes" : "No");
504fa030f66STakashi Iwai 	snd_iprintf(buffer, "Num Blocks: %d\n\n", ump->info.num_blocks);
505fa030f66STakashi Iwai 
506fa030f66STakashi Iwai 	list_for_each_entry(fb, &ump->block_list, list) {
507fa030f66STakashi Iwai 		snd_iprintf(buffer, "Block %d (%s)\n", fb->info.block_id,
508fa030f66STakashi Iwai 			    fb->info.name);
509fa030f66STakashi Iwai 		snd_iprintf(buffer, "  Direction: %s\n",
510fa030f66STakashi Iwai 			    ump_direction_string(fb->info.direction));
511fa030f66STakashi Iwai 		snd_iprintf(buffer, "  Active: %s\n",
512fa030f66STakashi Iwai 			    fb->info.active ? "Yes" : "No");
513fa030f66STakashi Iwai 		snd_iprintf(buffer, "  Groups: %d-%d\n",
514fa030f66STakashi Iwai 			    fb->info.first_group + 1,
515fa030f66STakashi Iwai 			    fb->info.first_group + fb->info.num_groups);
516fa030f66STakashi Iwai 		snd_iprintf(buffer, "  Is MIDI1: %s%s\n",
517fa030f66STakashi Iwai 			    (fb->info.flags & SNDRV_UMP_BLOCK_IS_MIDI1) ? "Yes" : "No",
518fa030f66STakashi Iwai 			    (fb->info.flags & SNDRV_UMP_BLOCK_IS_LOWSPEED) ? " (Low Speed)" : "");
519e375b8a0STakashi Iwai 		if (ump->info.version) {
520e375b8a0STakashi Iwai 			snd_iprintf(buffer, "  MIDI-CI Version: %d\n",
521e375b8a0STakashi Iwai 				    fb->info.midi_ci_version);
522e375b8a0STakashi Iwai 			snd_iprintf(buffer, "  Sysex8 Streams: %d\n",
523e375b8a0STakashi Iwai 				    fb->info.sysex8_streams);
524e375b8a0STakashi Iwai 			snd_iprintf(buffer, "  UI Hint: %s\n",
525e375b8a0STakashi Iwai 				    ump_ui_hint_string(fb->info.ui_hint));
526e375b8a0STakashi Iwai 		}
527fa030f66STakashi Iwai 		snd_iprintf(buffer, "\n");
528fa030f66STakashi Iwai 	}
529fa030f66STakashi Iwai }
530fa030f66STakashi Iwai 
5318ddb4126STakashi Iwai /* update dir_bits and active flag for all groups in the client */
snd_ump_update_group_attrs(struct snd_ump_endpoint * ump)5328bb7b689STakashi Iwai void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump)
5338ddb4126STakashi Iwai {
5348ddb4126STakashi Iwai 	struct snd_ump_block *fb;
5358ddb4126STakashi Iwai 	struct snd_ump_group *group;
5368ddb4126STakashi Iwai 	int i;
5378ddb4126STakashi Iwai 
5388ddb4126STakashi Iwai 	for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) {
5398ddb4126STakashi Iwai 		group = &ump->groups[i];
5408ddb4126STakashi Iwai 		*group->name = 0;
5418ddb4126STakashi Iwai 		group->dir_bits = 0;
5428ddb4126STakashi Iwai 		group->active = 0;
5438ddb4126STakashi Iwai 		group->group = i;
5448ddb4126STakashi Iwai 		group->valid = false;
5458ddb4126STakashi Iwai 	}
5468ddb4126STakashi Iwai 
5478ddb4126STakashi Iwai 	list_for_each_entry(fb, &ump->block_list, list) {
5488ddb4126STakashi Iwai 		if (fb->info.first_group + fb->info.num_groups > SNDRV_UMP_MAX_GROUPS)
5498ddb4126STakashi Iwai 			break;
5508ddb4126STakashi Iwai 		group = &ump->groups[fb->info.first_group];
5518ddb4126STakashi Iwai 		for (i = 0; i < fb->info.num_groups; i++, group++) {
5528ddb4126STakashi Iwai 			group->valid = true;
5538ddb4126STakashi Iwai 			if (fb->info.active)
5548ddb4126STakashi Iwai 				group->active = 1;
5558ddb4126STakashi Iwai 			switch (fb->info.direction) {
5568ddb4126STakashi Iwai 			case SNDRV_UMP_DIR_INPUT:
5578ddb4126STakashi Iwai 				group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_INPUT);
5588ddb4126STakashi Iwai 				break;
5598ddb4126STakashi Iwai 			case SNDRV_UMP_DIR_OUTPUT:
5608ddb4126STakashi Iwai 				group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_OUTPUT);
5618ddb4126STakashi Iwai 				break;
5628ddb4126STakashi Iwai 			case SNDRV_UMP_DIR_BIDIRECTION:
5638ddb4126STakashi Iwai 				group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_INPUT) |
5648ddb4126STakashi Iwai 					(1 << SNDRV_RAWMIDI_STREAM_OUTPUT);
5658ddb4126STakashi Iwai 				break;
5668ddb4126STakashi Iwai 			}
5678ddb4126STakashi Iwai 			if (!*fb->info.name)
5688ddb4126STakashi Iwai 				continue;
5698ddb4126STakashi Iwai 			if (!*group->name) {
5708ddb4126STakashi Iwai 				/* store the first matching name */
5718ddb4126STakashi Iwai 				strscpy(group->name, fb->info.name,
5728ddb4126STakashi Iwai 					sizeof(group->name));
5738ddb4126STakashi Iwai 			} else {
5748ddb4126STakashi Iwai 				/* when overlapping, concat names */
5758ddb4126STakashi Iwai 				strlcat(group->name, ", ", sizeof(group->name));
5768ddb4126STakashi Iwai 				strlcat(group->name, fb->info.name,
5778ddb4126STakashi Iwai 					sizeof(group->name));
5788ddb4126STakashi Iwai 			}
5798ddb4126STakashi Iwai 		}
5808ddb4126STakashi Iwai 	}
5818ddb4126STakashi Iwai }
5828bb7b689STakashi Iwai EXPORT_SYMBOL_GPL(snd_ump_update_group_attrs);
5838ddb4126STakashi Iwai 
58437e0e141STakashi Iwai /*
58537e0e141STakashi Iwai  * UMP endpoint and function block handling
58637e0e141STakashi Iwai  */
58737e0e141STakashi Iwai 
58837e0e141STakashi Iwai /* open / close UMP streams for the internal stream msg communication */
ump_request_open(struct snd_ump_endpoint * ump)58937e0e141STakashi Iwai static int ump_request_open(struct snd_ump_endpoint *ump)
59037e0e141STakashi Iwai {
59137e0e141STakashi Iwai 	return snd_rawmidi_kernel_open(&ump->core, 0,
59237e0e141STakashi Iwai 				       SNDRV_RAWMIDI_LFLG_OUTPUT,
59337e0e141STakashi Iwai 				       &ump->stream_rfile);
59437e0e141STakashi Iwai }
59537e0e141STakashi Iwai 
ump_request_close(struct snd_ump_endpoint * ump)59637e0e141STakashi Iwai static void ump_request_close(struct snd_ump_endpoint *ump)
59737e0e141STakashi Iwai {
59837e0e141STakashi Iwai 	snd_rawmidi_kernel_release(&ump->stream_rfile);
59937e0e141STakashi Iwai }
60037e0e141STakashi Iwai 
60137e0e141STakashi Iwai /* request a command and wait for the given response;
60237e0e141STakashi Iwai  * @req1 and @req2 are u32 commands
60337e0e141STakashi Iwai  * @reply is the expected UMP stream status
60437e0e141STakashi Iwai  */
ump_req_msg(struct snd_ump_endpoint * ump,u32 req1,u32 req2,u32 reply)60537e0e141STakashi Iwai static int ump_req_msg(struct snd_ump_endpoint *ump, u32 req1, u32 req2,
60637e0e141STakashi Iwai 		       u32 reply)
60737e0e141STakashi Iwai {
60837e0e141STakashi Iwai 	u32 buf[4];
60937e0e141STakashi Iwai 
61037e0e141STakashi Iwai 	ump_dbg(ump, "%s: request %08x %08x, wait-for %08x\n",
61137e0e141STakashi Iwai 		__func__, req1, req2, reply);
61237e0e141STakashi Iwai 	memset(buf, 0, sizeof(buf));
61337e0e141STakashi Iwai 	buf[0] = req1;
61437e0e141STakashi Iwai 	buf[1] = req2;
61537e0e141STakashi Iwai 	ump->stream_finished = 0;
61637e0e141STakashi Iwai 	ump->stream_wait_for = reply;
61737e0e141STakashi Iwai 	snd_rawmidi_kernel_write(ump->stream_rfile.output,
61837e0e141STakashi Iwai 				 (unsigned char *)&buf, 16);
61937e0e141STakashi Iwai 	wait_event_timeout(ump->stream_wait, ump->stream_finished,
62037e0e141STakashi Iwai 			   msecs_to_jiffies(500));
62137e0e141STakashi Iwai 	if (!READ_ONCE(ump->stream_finished)) {
62237e0e141STakashi Iwai 		ump_dbg(ump, "%s: request timed out\n", __func__);
62337e0e141STakashi Iwai 		return -ETIMEDOUT;
62437e0e141STakashi Iwai 	}
62537e0e141STakashi Iwai 	ump->stream_finished = 0;
62637e0e141STakashi Iwai 	ump_dbg(ump, "%s: reply: %08x %08x %08x %08x\n",
62737e0e141STakashi Iwai 		__func__, buf[0], buf[1], buf[2], buf[3]);
62837e0e141STakashi Iwai 	return 0;
62937e0e141STakashi Iwai }
63037e0e141STakashi Iwai 
63137e0e141STakashi Iwai /* append the received letters via UMP packet to the given string buffer;
63237e0e141STakashi Iwai  * return 1 if the full string is received or 0 to continue
63337e0e141STakashi Iwai  */
ump_append_string(struct snd_ump_endpoint * ump,char * dest,int maxsize,const u32 * buf,int offset)63437e0e141STakashi Iwai static int ump_append_string(struct snd_ump_endpoint *ump, char *dest,
63537e0e141STakashi Iwai 			     int maxsize, const u32 *buf, int offset)
63637e0e141STakashi Iwai {
63737e0e141STakashi Iwai 	unsigned char format;
63837e0e141STakashi Iwai 	int c;
63937e0e141STakashi Iwai 
64037e0e141STakashi Iwai 	format = ump_stream_message_format(buf[0]);
64137e0e141STakashi Iwai 	if (format == UMP_STREAM_MSG_FORMAT_SINGLE ||
64237e0e141STakashi Iwai 	    format == UMP_STREAM_MSG_FORMAT_START) {
64337e0e141STakashi Iwai 		c = 0;
64437e0e141STakashi Iwai 	} else {
64537e0e141STakashi Iwai 		c = strlen(dest);
64637e0e141STakashi Iwai 		if (c >= maxsize - 1)
64737e0e141STakashi Iwai 			return 1;
64837e0e141STakashi Iwai 	}
64937e0e141STakashi Iwai 
65037e0e141STakashi Iwai 	for (; offset < 16; offset++) {
65137e0e141STakashi Iwai 		dest[c] = buf[offset / 4] >> (3 - (offset % 4)) * 8;
65237e0e141STakashi Iwai 		if (!dest[c])
65337e0e141STakashi Iwai 			break;
65437e0e141STakashi Iwai 		if (++c >= maxsize - 1)
65537e0e141STakashi Iwai 			break;
65637e0e141STakashi Iwai 	}
65737e0e141STakashi Iwai 	dest[c] = 0;
65837e0e141STakashi Iwai 	return (format == UMP_STREAM_MSG_FORMAT_SINGLE ||
65937e0e141STakashi Iwai 		format == UMP_STREAM_MSG_FORMAT_END);
66037e0e141STakashi Iwai }
66137e0e141STakashi Iwai 
66237e0e141STakashi Iwai /* 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)66337e0e141STakashi Iwai static int ump_handle_ep_info_msg(struct snd_ump_endpoint *ump,
66437e0e141STakashi Iwai 				  const union snd_ump_stream_msg *buf)
66537e0e141STakashi Iwai {
66637e0e141STakashi Iwai 	ump->info.version = (buf->ep_info.ump_version_major << 8) |
66737e0e141STakashi Iwai 		buf->ep_info.ump_version_minor;
66837e0e141STakashi Iwai 	ump->info.num_blocks = buf->ep_info.num_function_blocks;
66937e0e141STakashi Iwai 	if (ump->info.num_blocks > SNDRV_UMP_MAX_BLOCKS) {
67037e0e141STakashi Iwai 		ump_info(ump, "Invalid function blocks %d, fallback to 1\n",
67137e0e141STakashi Iwai 			 ump->info.num_blocks);
67237e0e141STakashi Iwai 		ump->info.num_blocks = 1;
67337e0e141STakashi Iwai 	}
67437e0e141STakashi Iwai 
67501dfa8e9STakashi Iwai 	if (buf->ep_info.static_function_block)
67601dfa8e9STakashi Iwai 		ump->info.flags |= SNDRV_UMP_EP_INFO_STATIC_BLOCKS;
67701dfa8e9STakashi Iwai 
67837e0e141STakashi Iwai 	ump->info.protocol_caps = (buf->ep_info.protocol << 8) |
67937e0e141STakashi Iwai 		buf->ep_info.jrts;
68037e0e141STakashi Iwai 
68137e0e141STakashi Iwai 	ump_dbg(ump, "EP info: version=%x, num_blocks=%x, proto_caps=%x\n",
68237e0e141STakashi Iwai 		ump->info.version, ump->info.num_blocks, ump->info.protocol_caps);
68337e0e141STakashi Iwai 	return 1; /* finished */
68437e0e141STakashi Iwai }
68537e0e141STakashi Iwai 
68637e0e141STakashi Iwai /* 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)68737e0e141STakashi Iwai static int ump_handle_device_info_msg(struct snd_ump_endpoint *ump,
68837e0e141STakashi Iwai 				      const union snd_ump_stream_msg *buf)
68937e0e141STakashi Iwai {
69037e0e141STakashi Iwai 	ump->info.manufacturer_id = buf->device_info.manufacture_id & 0x7f7f7f;
69137e0e141STakashi Iwai 	ump->info.family_id = (buf->device_info.family_msb << 8) |
69237e0e141STakashi Iwai 		buf->device_info.family_lsb;
69337e0e141STakashi Iwai 	ump->info.model_id = (buf->device_info.model_msb << 8) |
69437e0e141STakashi Iwai 		buf->device_info.model_lsb;
69537e0e141STakashi Iwai 	ump->info.sw_revision[0] = (buf->device_info.sw_revision >> 24) & 0x7f;
69637e0e141STakashi Iwai 	ump->info.sw_revision[1] = (buf->device_info.sw_revision >> 16) & 0x7f;
69737e0e141STakashi Iwai 	ump->info.sw_revision[2] = (buf->device_info.sw_revision >> 8) & 0x7f;
69837e0e141STakashi Iwai 	ump->info.sw_revision[3] = buf->device_info.sw_revision & 0x7f;
69937e0e141STakashi Iwai 	ump_dbg(ump, "EP devinfo: manid=%08x, family=%04x, model=%04x, sw=%02x%02x%02x%02x\n",
70037e0e141STakashi Iwai 		ump->info.manufacturer_id,
70137e0e141STakashi Iwai 		ump->info.family_id,
70237e0e141STakashi Iwai 		ump->info.model_id,
70337e0e141STakashi Iwai 		ump->info.sw_revision[0],
70437e0e141STakashi Iwai 		ump->info.sw_revision[1],
70537e0e141STakashi Iwai 		ump->info.sw_revision[2],
70637e0e141STakashi Iwai 		ump->info.sw_revision[3]);
70737e0e141STakashi Iwai 	return 1; /* finished */
70837e0e141STakashi Iwai }
70937e0e141STakashi Iwai 
71037e0e141STakashi Iwai /* 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)71137e0e141STakashi Iwai static int ump_handle_ep_name_msg(struct snd_ump_endpoint *ump,
71237e0e141STakashi Iwai 				  const union snd_ump_stream_msg *buf)
71337e0e141STakashi Iwai {
71437e0e141STakashi Iwai 	return ump_append_string(ump, ump->info.name, sizeof(ump->info.name),
71537e0e141STakashi Iwai 				 buf->raw, 2);
71637e0e141STakashi Iwai }
71737e0e141STakashi Iwai 
71837e0e141STakashi Iwai /* 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)71937e0e141STakashi Iwai static int ump_handle_product_id_msg(struct snd_ump_endpoint *ump,
72037e0e141STakashi Iwai 				     const union snd_ump_stream_msg *buf)
72137e0e141STakashi Iwai {
72237e0e141STakashi Iwai 	return ump_append_string(ump, ump->info.product_id,
72337e0e141STakashi Iwai 				 sizeof(ump->info.product_id),
72437e0e141STakashi Iwai 				 buf->raw, 2);
72537e0e141STakashi Iwai }
72637e0e141STakashi Iwai 
7276a8b4800STakashi Iwai /* notify the protocol change to sequencer */
seq_notify_protocol(struct snd_ump_endpoint * ump)7286a8b4800STakashi Iwai static void seq_notify_protocol(struct snd_ump_endpoint *ump)
7296a8b4800STakashi Iwai {
7306a8b4800STakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQUENCER)
7316a8b4800STakashi Iwai 	if (ump->seq_ops && ump->seq_ops->switch_protocol)
7326a8b4800STakashi Iwai 		ump->seq_ops->switch_protocol(ump);
7336a8b4800STakashi Iwai #endif /* CONFIG_SND_SEQUENCER */
7346a8b4800STakashi Iwai }
7356a8b4800STakashi Iwai 
736a7980768STakashi Iwai /**
737a7980768STakashi Iwai  * snd_ump_switch_protocol - switch MIDI protocol
738a7980768STakashi Iwai  * @ump: UMP endpoint
739a7980768STakashi Iwai  * @protocol: protocol to switch to
740a7980768STakashi Iwai  *
741a7980768STakashi Iwai  * Returns 1 if the protocol is actually switched, 0 if unchanged
742a7980768STakashi Iwai  */
snd_ump_switch_protocol(struct snd_ump_endpoint * ump,unsigned int protocol)743a7980768STakashi Iwai int snd_ump_switch_protocol(struct snd_ump_endpoint *ump, unsigned int protocol)
744a7980768STakashi Iwai {
7450a900727STakashi Iwai 	unsigned int type;
7460a900727STakashi Iwai 
747a7980768STakashi Iwai 	protocol &= ump->info.protocol_caps;
748a7980768STakashi Iwai 	if (protocol == ump->info.protocol)
749a7980768STakashi Iwai 		return 0;
750a7980768STakashi Iwai 
7510a900727STakashi Iwai 	type = protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK;
7520a900727STakashi Iwai 	if (type != SNDRV_UMP_EP_INFO_PROTO_MIDI1 &&
7530a900727STakashi Iwai 	    type != SNDRV_UMP_EP_INFO_PROTO_MIDI2)
7540a900727STakashi Iwai 		return 0;
7550a900727STakashi Iwai 
756a7980768STakashi Iwai 	ump->info.protocol = protocol;
757a7980768STakashi Iwai 	ump_dbg(ump, "New protocol = %x (caps = %x)\n",
758a7980768STakashi Iwai 		protocol, ump->info.protocol_caps);
759a7980768STakashi Iwai 	seq_notify_protocol(ump);
760a7980768STakashi Iwai 	return 1;
761a7980768STakashi Iwai }
762a7980768STakashi Iwai EXPORT_SYMBOL_GPL(snd_ump_switch_protocol);
763a7980768STakashi Iwai 
76437e0e141STakashi Iwai /* 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)76537e0e141STakashi Iwai static int ump_handle_stream_cfg_msg(struct snd_ump_endpoint *ump,
76637e0e141STakashi Iwai 				     const union snd_ump_stream_msg *buf)
76737e0e141STakashi Iwai {
768a7980768STakashi Iwai 	unsigned int protocol =
76937e0e141STakashi Iwai 		(buf->stream_cfg.protocol << 8) | buf->stream_cfg.jrts;
770a7980768STakashi Iwai 
771a7980768STakashi Iwai 	snd_ump_switch_protocol(ump, protocol);
77237e0e141STakashi Iwai 	return 1; /* finished */
77337e0e141STakashi Iwai }
77437e0e141STakashi Iwai 
77537e0e141STakashi Iwai /* 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)77637e0e141STakashi Iwai static void fill_fb_info(struct snd_ump_endpoint *ump,
77737e0e141STakashi Iwai 			 struct snd_ump_block_info *info,
77837e0e141STakashi Iwai 			 const union snd_ump_stream_msg *buf)
77937e0e141STakashi Iwai {
78037e0e141STakashi Iwai 	info->direction = buf->fb_info.direction;
78137e0e141STakashi Iwai 	info->ui_hint = buf->fb_info.ui_hint;
78237e0e141STakashi Iwai 	info->first_group = buf->fb_info.first_group;
78337e0e141STakashi Iwai 	info->num_groups = buf->fb_info.num_groups;
7840357abf9STakashi Iwai 	if (buf->fb_info.midi_10 < 2)
78537e0e141STakashi Iwai 		info->flags = buf->fb_info.midi_10;
7860357abf9STakashi Iwai 	else
7870357abf9STakashi Iwai 		info->flags = SNDRV_UMP_BLOCK_IS_MIDI1 | SNDRV_UMP_BLOCK_IS_LOWSPEED;
78837e0e141STakashi Iwai 	info->active = buf->fb_info.active;
78937e0e141STakashi Iwai 	info->midi_ci_version = buf->fb_info.midi_ci_version;
79037e0e141STakashi Iwai 	info->sysex8_streams = buf->fb_info.sysex8_streams;
79137e0e141STakashi Iwai 
79237e0e141STakashi Iwai 	ump_dbg(ump, "FB %d: dir=%d, active=%d, first_gp=%d, num_gp=%d, midici=%d, sysex8=%d, flags=0x%x\n",
79337e0e141STakashi Iwai 		info->block_id, info->direction, info->active,
79437e0e141STakashi Iwai 		info->first_group, info->num_groups, info->midi_ci_version,
79537e0e141STakashi Iwai 		info->sysex8_streams, info->flags);
796689e0780STakashi Iwai 
797689e0780STakashi Iwai 	if ((info->flags & SNDRV_UMP_BLOCK_IS_MIDI1) && info->num_groups != 1) {
798689e0780STakashi Iwai 		info->num_groups = 1;
799689e0780STakashi Iwai 		ump_dbg(ump, "FB %d: corrected groups to 1 for MIDI1\n",
800689e0780STakashi Iwai 			info->block_id);
801689e0780STakashi Iwai 	}
80237e0e141STakashi Iwai }
80337e0e141STakashi Iwai 
8044a16a3afSTakashi Iwai /* 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)8054a16a3afSTakashi Iwai static bool is_fb_info_updated(struct snd_ump_endpoint *ump,
8064a16a3afSTakashi Iwai 			       struct snd_ump_block *fb,
8074a16a3afSTakashi Iwai 			       const union snd_ump_stream_msg *buf)
8084a16a3afSTakashi Iwai {
8094a16a3afSTakashi Iwai 	char tmpbuf[offsetof(struct snd_ump_block_info, name)];
8104a16a3afSTakashi Iwai 
81101dfa8e9STakashi Iwai 	if (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) {
81201dfa8e9STakashi Iwai 		ump_info(ump, "Skipping static FB info update (blk#%d)\n",
81301dfa8e9STakashi Iwai 			 fb->info.block_id);
81401dfa8e9STakashi Iwai 		return 0;
81501dfa8e9STakashi Iwai 	}
81601dfa8e9STakashi Iwai 
8174a16a3afSTakashi Iwai 	memcpy(tmpbuf, &fb->info, sizeof(tmpbuf));
8184a16a3afSTakashi Iwai 	fill_fb_info(ump, (struct snd_ump_block_info *)tmpbuf, buf);
8194a16a3afSTakashi Iwai 	return memcmp(&fb->info, tmpbuf, sizeof(tmpbuf)) != 0;
8204a16a3afSTakashi Iwai }
8214a16a3afSTakashi Iwai 
8224a16a3afSTakashi Iwai /* notify the FB info/name change to sequencer */
seq_notify_fb_change(struct snd_ump_endpoint * ump,struct snd_ump_block * fb)8234a16a3afSTakashi Iwai static void seq_notify_fb_change(struct snd_ump_endpoint *ump,
8244a16a3afSTakashi Iwai 				 struct snd_ump_block *fb)
8254a16a3afSTakashi Iwai {
8264a16a3afSTakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQUENCER)
8274a16a3afSTakashi Iwai 	if (ump->seq_ops && ump->seq_ops->notify_fb_change)
8284a16a3afSTakashi Iwai 		ump->seq_ops->notify_fb_change(ump, fb);
8294a16a3afSTakashi Iwai #endif
8304a16a3afSTakashi Iwai }
8314a16a3afSTakashi Iwai 
83237e0e141STakashi Iwai /* 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)83337e0e141STakashi Iwai static int ump_handle_fb_info_msg(struct snd_ump_endpoint *ump,
83437e0e141STakashi Iwai 				  const union snd_ump_stream_msg *buf)
83537e0e141STakashi Iwai {
83637e0e141STakashi Iwai 	unsigned char blk;
83737e0e141STakashi Iwai 	struct snd_ump_block *fb;
83837e0e141STakashi Iwai 
83937e0e141STakashi Iwai 	blk = buf->fb_info.function_block_id;
84037e0e141STakashi Iwai 	fb = snd_ump_get_block(ump, blk);
8414a16a3afSTakashi Iwai 
84237e0e141STakashi Iwai 	/* complain only if updated after parsing */
8434a16a3afSTakashi Iwai 	if (!fb && ump->parsed) {
84437e0e141STakashi Iwai 		ump_info(ump, "Function Block Info Update for non-existing block %d\n",
84537e0e141STakashi Iwai 			 blk);
84637e0e141STakashi Iwai 		return -ENODEV;
84737e0e141STakashi Iwai 	}
8484a16a3afSTakashi Iwai 
8494a16a3afSTakashi Iwai 	/* When updated after the initial parse, check the FB info update */
8504a16a3afSTakashi Iwai 	if (ump->parsed && !is_fb_info_updated(ump, fb, buf))
8514a16a3afSTakashi Iwai 		return 1; /* no content change */
8524a16a3afSTakashi Iwai 
8534a16a3afSTakashi Iwai 	if (fb) {
8544a16a3afSTakashi Iwai 		fill_fb_info(ump, &fb->info, buf);
8558ddb4126STakashi Iwai 		if (ump->parsed) {
8568bb7b689STakashi Iwai 			snd_ump_update_group_attrs(ump);
8578d891c86STakashi Iwai 			update_legacy_names(ump);
8584a16a3afSTakashi Iwai 			seq_notify_fb_change(ump, fb);
8594a16a3afSTakashi Iwai 		}
8608ddb4126STakashi Iwai 	}
8614a16a3afSTakashi Iwai 
86237e0e141STakashi Iwai 	return 1; /* finished */
86337e0e141STakashi Iwai }
86437e0e141STakashi Iwai 
86537e0e141STakashi Iwai /* 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)86637e0e141STakashi Iwai static int ump_handle_fb_name_msg(struct snd_ump_endpoint *ump,
86737e0e141STakashi Iwai 				  const union snd_ump_stream_msg *buf)
86837e0e141STakashi Iwai {
86937e0e141STakashi Iwai 	unsigned char blk;
87037e0e141STakashi Iwai 	struct snd_ump_block *fb;
8714a16a3afSTakashi Iwai 	int ret;
87237e0e141STakashi Iwai 
87337e0e141STakashi Iwai 	blk = buf->fb_name.function_block_id;
87437e0e141STakashi Iwai 	fb = snd_ump_get_block(ump, blk);
87537e0e141STakashi Iwai 	if (!fb)
87637e0e141STakashi Iwai 		return -ENODEV;
87737e0e141STakashi Iwai 
878ad4ab148STakashi Iwai 	if (ump->parsed &&
879ad4ab148STakashi Iwai 	    (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS)) {
880ad4ab148STakashi Iwai 		ump_dbg(ump, "Skipping static FB name update (blk#%d)\n",
881ad4ab148STakashi Iwai 			fb->info.block_id);
882ad4ab148STakashi Iwai 		return 0;
883ad4ab148STakashi Iwai 	}
884ad4ab148STakashi Iwai 
8854a16a3afSTakashi Iwai 	ret = ump_append_string(ump, fb->info.name, sizeof(fb->info.name),
88637e0e141STakashi Iwai 				buf->raw, 3);
8874a16a3afSTakashi Iwai 	/* notify the FB name update to sequencer, too */
8888ddb4126STakashi Iwai 	if (ret > 0 && ump->parsed) {
8898bb7b689STakashi Iwai 		snd_ump_update_group_attrs(ump);
8908d891c86STakashi Iwai 		update_legacy_names(ump);
8914a16a3afSTakashi Iwai 		seq_notify_fb_change(ump, fb);
8928ddb4126STakashi Iwai 	}
8934a16a3afSTakashi Iwai 	return ret;
89437e0e141STakashi Iwai }
89537e0e141STakashi Iwai 
create_block_from_fb_info(struct snd_ump_endpoint * ump,int blk)89637e0e141STakashi Iwai static int create_block_from_fb_info(struct snd_ump_endpoint *ump, int blk)
89737e0e141STakashi Iwai {
89837e0e141STakashi Iwai 	struct snd_ump_block *fb;
89937e0e141STakashi Iwai 	unsigned char direction, first_group, num_groups;
90037e0e141STakashi Iwai 	const union snd_ump_stream_msg *buf =
90137e0e141STakashi Iwai 		(const union snd_ump_stream_msg *)ump->input_buf;
90237e0e141STakashi Iwai 	u32 msg;
90337e0e141STakashi Iwai 	int err;
90437e0e141STakashi Iwai 
90537e0e141STakashi Iwai 	/* query the FB info once */
90637e0e141STakashi Iwai 	msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_FB_DISCOVERY, 0) |
90737e0e141STakashi Iwai 		(blk << 8) | UMP_STREAM_MSG_REQUEST_FB_INFO;
90837e0e141STakashi Iwai 	err = ump_req_msg(ump, msg, 0, UMP_STREAM_MSG_STATUS_FB_INFO);
90937e0e141STakashi Iwai 	if (err < 0) {
91037e0e141STakashi Iwai 		ump_dbg(ump, "Unable to get FB info for block %d\n", blk);
91137e0e141STakashi Iwai 		return err;
91237e0e141STakashi Iwai 	}
91337e0e141STakashi Iwai 
91437e0e141STakashi Iwai 	/* the last input must be the FB info */
91537e0e141STakashi Iwai 	if (buf->fb_info.status != UMP_STREAM_MSG_STATUS_FB_INFO) {
91637e0e141STakashi Iwai 		ump_dbg(ump, "Inconsistent input: 0x%x\n", *buf->raw);
91737e0e141STakashi Iwai 		return -EINVAL;
91837e0e141STakashi Iwai 	}
91937e0e141STakashi Iwai 
92037e0e141STakashi Iwai 	direction = buf->fb_info.direction;
92137e0e141STakashi Iwai 	first_group = buf->fb_info.first_group;
92237e0e141STakashi Iwai 	num_groups = buf->fb_info.num_groups;
92337e0e141STakashi Iwai 
92437e0e141STakashi Iwai 	err = snd_ump_block_new(ump, blk, direction, first_group, num_groups,
92537e0e141STakashi Iwai 				&fb);
92637e0e141STakashi Iwai 	if (err < 0)
92737e0e141STakashi Iwai 		return err;
92837e0e141STakashi Iwai 
92937e0e141STakashi Iwai 	fill_fb_info(ump, &fb->info, buf);
93037e0e141STakashi Iwai 
93137e0e141STakashi Iwai 	msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_FB_DISCOVERY, 0) |
93237e0e141STakashi Iwai 		(blk << 8) | UMP_STREAM_MSG_REQUEST_FB_NAME;
93337e0e141STakashi Iwai 	err = ump_req_msg(ump, msg, 0, UMP_STREAM_MSG_STATUS_FB_NAME);
93437e0e141STakashi Iwai 	if (err)
93537e0e141STakashi Iwai 		ump_dbg(ump, "Unable to get UMP FB name string #%d\n", blk);
93637e0e141STakashi Iwai 
93737e0e141STakashi Iwai 	return 0;
93837e0e141STakashi Iwai }
93937e0e141STakashi Iwai 
94037e0e141STakashi Iwai /* handle stream messages, called from snd_ump_receive() */
ump_handle_stream_msg(struct snd_ump_endpoint * ump,const u32 * buf,int size)94137e0e141STakashi Iwai static void ump_handle_stream_msg(struct snd_ump_endpoint *ump,
94237e0e141STakashi Iwai 				  const u32 *buf, int size)
94337e0e141STakashi Iwai {
94437e0e141STakashi Iwai 	const union snd_ump_stream_msg *msg;
94537e0e141STakashi Iwai 	unsigned int status;
94637e0e141STakashi Iwai 	int ret;
94737e0e141STakashi Iwai 
948eacd9c7fSTakashi Iwai 	/* UMP stream message suppressed (for gadget UMP)? */
949eacd9c7fSTakashi Iwai 	if (ump->no_process_stream)
950eacd9c7fSTakashi Iwai 		return;
951eacd9c7fSTakashi Iwai 
95237e0e141STakashi Iwai 	BUILD_BUG_ON(sizeof(*msg) != 16);
95337e0e141STakashi Iwai 	ump_dbg(ump, "Stream msg: %08x %08x %08x %08x\n",
95437e0e141STakashi Iwai 		buf[0], buf[1], buf[2], buf[3]);
95537e0e141STakashi Iwai 
95637e0e141STakashi Iwai 	if (size != 4 || ump_message_type(*buf) != UMP_MSG_TYPE_STREAM)
95737e0e141STakashi Iwai 		return;
95837e0e141STakashi Iwai 
95937e0e141STakashi Iwai 	msg = (const union snd_ump_stream_msg *)buf;
96037e0e141STakashi Iwai 	status = ump_stream_message_status(*buf);
96137e0e141STakashi Iwai 	switch (status) {
96237e0e141STakashi Iwai 	case UMP_STREAM_MSG_STATUS_EP_INFO:
96337e0e141STakashi Iwai 		ret = ump_handle_ep_info_msg(ump, msg);
96437e0e141STakashi Iwai 		break;
96537e0e141STakashi Iwai 	case UMP_STREAM_MSG_STATUS_DEVICE_INFO:
96637e0e141STakashi Iwai 		ret = ump_handle_device_info_msg(ump, msg);
96737e0e141STakashi Iwai 		break;
96837e0e141STakashi Iwai 	case UMP_STREAM_MSG_STATUS_EP_NAME:
96937e0e141STakashi Iwai 		ret = ump_handle_ep_name_msg(ump, msg);
97037e0e141STakashi Iwai 		break;
97137e0e141STakashi Iwai 	case UMP_STREAM_MSG_STATUS_PRODUCT_ID:
97237e0e141STakashi Iwai 		ret = ump_handle_product_id_msg(ump, msg);
97337e0e141STakashi Iwai 		break;
97437e0e141STakashi Iwai 	case UMP_STREAM_MSG_STATUS_STREAM_CFG:
97537e0e141STakashi Iwai 		ret = ump_handle_stream_cfg_msg(ump, msg);
97637e0e141STakashi Iwai 		break;
97737e0e141STakashi Iwai 	case UMP_STREAM_MSG_STATUS_FB_INFO:
97837e0e141STakashi Iwai 		ret = ump_handle_fb_info_msg(ump, msg);
97937e0e141STakashi Iwai 		break;
98037e0e141STakashi Iwai 	case UMP_STREAM_MSG_STATUS_FB_NAME:
98137e0e141STakashi Iwai 		ret = ump_handle_fb_name_msg(ump, msg);
98237e0e141STakashi Iwai 		break;
98337e0e141STakashi Iwai 	default:
98437e0e141STakashi Iwai 		return;
98537e0e141STakashi Iwai 	}
98637e0e141STakashi Iwai 
98737e0e141STakashi Iwai 	/* when the message has been processed fully, wake up */
98837e0e141STakashi Iwai 	if (ret > 0 && ump->stream_wait_for == status) {
98937e0e141STakashi Iwai 		WRITE_ONCE(ump->stream_finished, 1);
99037e0e141STakashi Iwai 		wake_up(&ump->stream_wait);
99137e0e141STakashi Iwai 	}
99237e0e141STakashi Iwai }
99337e0e141STakashi Iwai 
99437e0e141STakashi Iwai /**
99537e0e141STakashi Iwai  * snd_ump_parse_endpoint - parse endpoint and create function blocks
99637e0e141STakashi Iwai  * @ump: UMP object
99737e0e141STakashi Iwai  *
99837e0e141STakashi Iwai  * Returns 0 for successful parse, -ENODEV if device doesn't respond
99937e0e141STakashi Iwai  * (or the query is unsupported), or other error code for serious errors.
100037e0e141STakashi Iwai  */
snd_ump_parse_endpoint(struct snd_ump_endpoint * ump)100137e0e141STakashi Iwai int snd_ump_parse_endpoint(struct snd_ump_endpoint *ump)
100237e0e141STakashi Iwai {
100337e0e141STakashi Iwai 	int blk, err;
100437e0e141STakashi Iwai 	u32 msg;
100537e0e141STakashi Iwai 
100637e0e141STakashi Iwai 	if (!(ump->core.info_flags & SNDRV_RAWMIDI_INFO_DUPLEX))
100737e0e141STakashi Iwai 		return -ENODEV;
100837e0e141STakashi Iwai 
100937e0e141STakashi Iwai 	err = ump_request_open(ump);
101037e0e141STakashi Iwai 	if (err < 0) {
101137e0e141STakashi Iwai 		ump_dbg(ump, "Unable to open rawmidi device: %d\n", err);
101237e0e141STakashi Iwai 		return err;
101337e0e141STakashi Iwai 	}
101437e0e141STakashi Iwai 
101537e0e141STakashi Iwai 	/* Check Endpoint Information */
101637e0e141STakashi Iwai 	msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_EP_DISCOVERY, 0) |
101737e0e141STakashi Iwai 		0x0101; /* UMP version 1.1 */
101837e0e141STakashi Iwai 	err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_EP_INFO,
101937e0e141STakashi Iwai 			  UMP_STREAM_MSG_STATUS_EP_INFO);
102037e0e141STakashi Iwai 	if (err < 0) {
102137e0e141STakashi Iwai 		ump_dbg(ump, "Unable to get UMP EP info\n");
102237e0e141STakashi Iwai 		goto error;
102337e0e141STakashi Iwai 	}
102437e0e141STakashi Iwai 
102537e0e141STakashi Iwai 	/* Request Endpoint Device Info */
102637e0e141STakashi Iwai 	err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_DEVICE_INFO,
102737e0e141STakashi Iwai 			  UMP_STREAM_MSG_STATUS_DEVICE_INFO);
102837e0e141STakashi Iwai 	if (err < 0)
102937e0e141STakashi Iwai 		ump_dbg(ump, "Unable to get UMP EP device info\n");
103037e0e141STakashi Iwai 
103137e0e141STakashi Iwai 	/* Request Endpoint Name */
103237e0e141STakashi Iwai 	err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_EP_NAME,
103337e0e141STakashi Iwai 			  UMP_STREAM_MSG_STATUS_EP_NAME);
103437e0e141STakashi Iwai 	if (err < 0)
103537e0e141STakashi Iwai 		ump_dbg(ump, "Unable to get UMP EP name string\n");
103637e0e141STakashi Iwai 
103737e0e141STakashi Iwai 	/* Request Endpoint Product ID */
103837e0e141STakashi Iwai 	err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_PRODUCT_ID,
103937e0e141STakashi Iwai 			  UMP_STREAM_MSG_STATUS_PRODUCT_ID);
104037e0e141STakashi Iwai 	if (err < 0)
104137e0e141STakashi Iwai 		ump_dbg(ump, "Unable to get UMP EP product ID string\n");
104237e0e141STakashi Iwai 
104337e0e141STakashi Iwai 	/* Get the current stream configuration */
104437e0e141STakashi Iwai 	err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_STREAM_CFG,
104537e0e141STakashi Iwai 			  UMP_STREAM_MSG_STATUS_STREAM_CFG);
104637e0e141STakashi Iwai 	if (err < 0)
104737e0e141STakashi Iwai 		ump_dbg(ump, "Unable to get UMP EP stream config\n");
104837e0e141STakashi Iwai 
104949458c09STakashi Iwai 	/* If no protocol is set by some reason, assume the valid one */
105049458c09STakashi Iwai 	if (!(ump->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK)) {
105149458c09STakashi Iwai 		if (ump->info.protocol_caps & SNDRV_UMP_EP_INFO_PROTO_MIDI2)
105249458c09STakashi Iwai 			ump->info.protocol |= SNDRV_UMP_EP_INFO_PROTO_MIDI2;
105349458c09STakashi Iwai 		else if (ump->info.protocol_caps & SNDRV_UMP_EP_INFO_PROTO_MIDI1)
105449458c09STakashi Iwai 			ump->info.protocol |= SNDRV_UMP_EP_INFO_PROTO_MIDI1;
105549458c09STakashi Iwai 	}
105649458c09STakashi Iwai 
105737e0e141STakashi Iwai 	/* Query and create blocks from Function Blocks */
105837e0e141STakashi Iwai 	for (blk = 0; blk < ump->info.num_blocks; blk++) {
105937e0e141STakashi Iwai 		err = create_block_from_fb_info(ump, blk);
106037e0e141STakashi Iwai 		if (err < 0)
106137e0e141STakashi Iwai 			continue;
106237e0e141STakashi Iwai 	}
106337e0e141STakashi Iwai 
10648ddb4126STakashi Iwai 	/* initialize group attributions */
10658bb7b689STakashi Iwai 	snd_ump_update_group_attrs(ump);
10668ddb4126STakashi Iwai 
106737e0e141STakashi Iwai  error:
106837e0e141STakashi Iwai 	ump->parsed = true;
106937e0e141STakashi Iwai 	ump_request_close(ump);
107037e0e141STakashi Iwai 	if (err == -ETIMEDOUT)
107137e0e141STakashi Iwai 		err = -ENODEV;
107237e0e141STakashi Iwai 	return err;
107337e0e141STakashi Iwai }
107437e0e141STakashi Iwai EXPORT_SYMBOL_GPL(snd_ump_parse_endpoint);
107537e0e141STakashi Iwai 
10760b5288f5STakashi Iwai #if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
10770b5288f5STakashi Iwai /*
10780b5288f5STakashi Iwai  * Legacy rawmidi support
10790b5288f5STakashi Iwai  */
snd_ump_legacy_open(struct snd_rawmidi_substream * substream)10800b5288f5STakashi Iwai static int snd_ump_legacy_open(struct snd_rawmidi_substream *substream)
10810b5288f5STakashi Iwai {
10820b5288f5STakashi Iwai 	struct snd_ump_endpoint *ump = substream->rmidi->private_data;
10830b5288f5STakashi Iwai 	int dir = substream->stream;
1084b2bcbd03STakashi Iwai 	int group = ump->legacy_mapping[substream->number];
1085b5e175e1STakashi Iwai 	int err;
10860b5288f5STakashi Iwai 
1087b5e175e1STakashi Iwai 	guard(mutex)(&ump->open_mutex);
1088b5e175e1STakashi Iwai 	if (ump->legacy_substreams[dir][group])
1089b5e175e1STakashi Iwai 		return -EBUSY;
1090cf29cbf6STakashi Iwai 	if (!ump->groups[group].active)
1091cf29cbf6STakashi Iwai 		return -ENODEV;
10920b5288f5STakashi Iwai 	if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
10930b5288f5STakashi Iwai 		if (!ump->legacy_out_opens) {
10940b5288f5STakashi Iwai 			err = snd_rawmidi_kernel_open(&ump->core, 0,
10950b5288f5STakashi Iwai 						      SNDRV_RAWMIDI_LFLG_OUTPUT |
10960b5288f5STakashi Iwai 						      SNDRV_RAWMIDI_LFLG_APPEND,
10970b5288f5STakashi Iwai 						      &ump->legacy_out_rfile);
10980b5288f5STakashi Iwai 			if (err < 0)
1099b5e175e1STakashi Iwai 				return err;
11000b5288f5STakashi Iwai 		}
11010b5288f5STakashi Iwai 		ump->legacy_out_opens++;
110233cd7630STakashi Iwai 		snd_ump_convert_reset(&ump->out_cvts[group]);
11030b5288f5STakashi Iwai 	}
1104b5e175e1STakashi Iwai 	guard(spinlock_irq)(&ump->legacy_locks[dir]);
11050b5288f5STakashi Iwai 	ump->legacy_substreams[dir][group] = substream;
1106b5e175e1STakashi Iwai 	return 0;
11070b5288f5STakashi Iwai }
11080b5288f5STakashi Iwai 
snd_ump_legacy_close(struct snd_rawmidi_substream * substream)11090b5288f5STakashi Iwai static int snd_ump_legacy_close(struct snd_rawmidi_substream *substream)
11100b5288f5STakashi Iwai {
11110b5288f5STakashi Iwai 	struct snd_ump_endpoint *ump = substream->rmidi->private_data;
11120b5288f5STakashi Iwai 	int dir = substream->stream;
1113b2bcbd03STakashi Iwai 	int group = ump->legacy_mapping[substream->number];
11140b5288f5STakashi Iwai 
1115b5e175e1STakashi Iwai 	guard(mutex)(&ump->open_mutex);
1116b5e175e1STakashi Iwai 	scoped_guard(spinlock_irq, &ump->legacy_locks[dir])
11170b5288f5STakashi Iwai 		ump->legacy_substreams[dir][group] = NULL;
11180b5288f5STakashi Iwai 	if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
11190b5288f5STakashi Iwai 		if (!--ump->legacy_out_opens)
11200b5288f5STakashi Iwai 			snd_rawmidi_kernel_release(&ump->legacy_out_rfile);
11210b5288f5STakashi Iwai 	}
11220b5288f5STakashi Iwai 	return 0;
11230b5288f5STakashi Iwai }
11240b5288f5STakashi Iwai 
snd_ump_legacy_trigger(struct snd_rawmidi_substream * substream,int up)11250b5288f5STakashi Iwai static void snd_ump_legacy_trigger(struct snd_rawmidi_substream *substream,
11260b5288f5STakashi Iwai 				   int up)
11270b5288f5STakashi Iwai {
11280b5288f5STakashi Iwai 	struct snd_ump_endpoint *ump = substream->rmidi->private_data;
11290b5288f5STakashi Iwai 	int dir = substream->stream;
11300b5288f5STakashi Iwai 
11310b5288f5STakashi Iwai 	ump->ops->trigger(ump, dir, up);
11320b5288f5STakashi Iwai }
11330b5288f5STakashi Iwai 
snd_ump_legacy_drain(struct snd_rawmidi_substream * substream)11340b5288f5STakashi Iwai static void snd_ump_legacy_drain(struct snd_rawmidi_substream *substream)
11350b5288f5STakashi Iwai {
11360b5288f5STakashi Iwai 	struct snd_ump_endpoint *ump = substream->rmidi->private_data;
11370b5288f5STakashi Iwai 
11380b5288f5STakashi Iwai 	if (ump->ops->drain)
11390b5288f5STakashi Iwai 		ump->ops->drain(ump, SNDRV_RAWMIDI_STREAM_OUTPUT);
11400b5288f5STakashi Iwai }
11410b5288f5STakashi Iwai 
snd_ump_legacy_dev_register(struct snd_rawmidi * rmidi)11420b5288f5STakashi Iwai static int snd_ump_legacy_dev_register(struct snd_rawmidi *rmidi)
11430b5288f5STakashi Iwai {
11440b5288f5STakashi Iwai 	/* dummy, just for avoiding create superfluous seq clients */
11450b5288f5STakashi Iwai 	return 0;
11460b5288f5STakashi Iwai }
11470b5288f5STakashi Iwai 
11480b5288f5STakashi Iwai static const struct snd_rawmidi_ops snd_ump_legacy_input_ops = {
11490b5288f5STakashi Iwai 	.open = snd_ump_legacy_open,
11500b5288f5STakashi Iwai 	.close = snd_ump_legacy_close,
11510b5288f5STakashi Iwai 	.trigger = snd_ump_legacy_trigger,
11520b5288f5STakashi Iwai };
11530b5288f5STakashi Iwai 
11540b5288f5STakashi Iwai static const struct snd_rawmidi_ops snd_ump_legacy_output_ops = {
11550b5288f5STakashi Iwai 	.open = snd_ump_legacy_open,
11560b5288f5STakashi Iwai 	.close = snd_ump_legacy_close,
11570b5288f5STakashi Iwai 	.trigger = snd_ump_legacy_trigger,
11580b5288f5STakashi Iwai 	.drain = snd_ump_legacy_drain,
11590b5288f5STakashi Iwai };
11600b5288f5STakashi Iwai 
11610b5288f5STakashi Iwai static const struct snd_rawmidi_global_ops snd_ump_legacy_ops = {
11620b5288f5STakashi Iwai 	.dev_register = snd_ump_legacy_dev_register,
11630b5288f5STakashi Iwai };
11640b5288f5STakashi Iwai 
process_legacy_output(struct snd_ump_endpoint * ump,u32 * buffer,int count)11650b5288f5STakashi Iwai static int process_legacy_output(struct snd_ump_endpoint *ump,
11660b5288f5STakashi Iwai 				 u32 *buffer, int count)
11670b5288f5STakashi Iwai {
11680b5288f5STakashi Iwai 	struct snd_rawmidi_substream *substream;
11690b5288f5STakashi Iwai 	struct ump_cvt_to_ump *ctx;
11700b5288f5STakashi Iwai 	const int dir = SNDRV_RAWMIDI_STREAM_OUTPUT;
11710b5288f5STakashi Iwai 	unsigned char c;
11720b5288f5STakashi Iwai 	int group, size = 0;
11730b5288f5STakashi Iwai 
11740b5288f5STakashi Iwai 	if (!ump->out_cvts || !ump->legacy_out_opens)
11750b5288f5STakashi Iwai 		return 0;
11760b5288f5STakashi Iwai 
1177b5e175e1STakashi Iwai 	guard(spinlock_irqsave)(&ump->legacy_locks[dir]);
11780b5288f5STakashi Iwai 	for (group = 0; group < SNDRV_UMP_MAX_GROUPS; group++) {
11790b5288f5STakashi Iwai 		substream = ump->legacy_substreams[dir][group];
11800b5288f5STakashi Iwai 		if (!substream)
11810b5288f5STakashi Iwai 			continue;
11820b5288f5STakashi Iwai 		ctx = &ump->out_cvts[group];
11830b5288f5STakashi Iwai 		while (!ctx->ump_bytes &&
11840b5288f5STakashi Iwai 		       snd_rawmidi_transmit(substream, &c, 1) > 0)
118533cd7630STakashi Iwai 			snd_ump_convert_to_ump(ctx, group, ump->info.protocol, c);
11860b5288f5STakashi Iwai 		if (ctx->ump_bytes && ctx->ump_bytes <= count) {
11870b5288f5STakashi Iwai 			size = ctx->ump_bytes;
11880b5288f5STakashi Iwai 			memcpy(buffer, ctx->ump, size);
11890b5288f5STakashi Iwai 			ctx->ump_bytes = 0;
11900b5288f5STakashi Iwai 			break;
11910b5288f5STakashi Iwai 		}
11920b5288f5STakashi Iwai 	}
11930b5288f5STakashi Iwai 	return size;
11940b5288f5STakashi Iwai }
11950b5288f5STakashi Iwai 
process_legacy_input(struct snd_ump_endpoint * ump,const u32 * src,int words)11960b5288f5STakashi Iwai static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src,
11970b5288f5STakashi Iwai 				 int words)
11980b5288f5STakashi Iwai {
11990b5288f5STakashi Iwai 	struct snd_rawmidi_substream *substream;
12000b5288f5STakashi Iwai 	unsigned char buf[16];
12010b5288f5STakashi Iwai 	unsigned char group;
12020b5288f5STakashi Iwai 	const int dir = SNDRV_RAWMIDI_STREAM_INPUT;
12030b5288f5STakashi Iwai 	int size;
12040b5288f5STakashi Iwai 
120533cd7630STakashi Iwai 	size = snd_ump_convert_from_ump(src, buf, &group);
12060b5288f5STakashi Iwai 	if (size <= 0)
12070b5288f5STakashi Iwai 		return;
1208b5e175e1STakashi Iwai 	guard(spinlock_irqsave)(&ump->legacy_locks[dir]);
12090b5288f5STakashi Iwai 	substream = ump->legacy_substreams[dir][group];
12100b5288f5STakashi Iwai 	if (substream)
12110b5288f5STakashi Iwai 		snd_rawmidi_receive(substream, buf, size);
12120b5288f5STakashi Iwai }
12130b5288f5STakashi Iwai 
1214b2bcbd03STakashi Iwai /* Fill ump->legacy_mapping[] for groups to be used for legacy rawmidi */
fill_legacy_mapping(struct snd_ump_endpoint * ump)1215b2bcbd03STakashi Iwai static int fill_legacy_mapping(struct snd_ump_endpoint *ump)
1216b2bcbd03STakashi Iwai {
1217b2bcbd03STakashi Iwai 	struct snd_ump_block *fb;
1218b2bcbd03STakashi Iwai 	unsigned int group_maps = 0;
1219b2bcbd03STakashi Iwai 	int i, num;
1220b2bcbd03STakashi Iwai 
1221b2bcbd03STakashi Iwai 	if (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) {
1222b2bcbd03STakashi Iwai 		list_for_each_entry(fb, &ump->block_list, list) {
1223b2bcbd03STakashi Iwai 			for (i = 0; i < fb->info.num_groups; i++)
1224b2bcbd03STakashi Iwai 				group_maps |= 1U << (fb->info.first_group + i);
1225b2bcbd03STakashi Iwai 		}
1226b2bcbd03STakashi Iwai 		if (!group_maps)
1227b2bcbd03STakashi Iwai 			ump_info(ump, "No UMP Group is found in FB\n");
1228b2bcbd03STakashi Iwai 	}
1229b2bcbd03STakashi Iwai 
1230b2bcbd03STakashi Iwai 	/* use all groups for non-static case */
1231b2bcbd03STakashi Iwai 	if (!group_maps)
1232b2bcbd03STakashi Iwai 		group_maps = (1U << SNDRV_UMP_MAX_GROUPS) - 1;
1233b2bcbd03STakashi Iwai 
1234b2bcbd03STakashi Iwai 	num = 0;
1235b2bcbd03STakashi Iwai 	for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++)
1236b2bcbd03STakashi Iwai 		if (group_maps & (1U << i))
1237b2bcbd03STakashi Iwai 			ump->legacy_mapping[num++] = i;
1238b2bcbd03STakashi Iwai 
1239b2bcbd03STakashi Iwai 	return num;
1240b2bcbd03STakashi Iwai }
1241b2bcbd03STakashi Iwai 
fill_substream_names(struct snd_ump_endpoint * ump,struct snd_rawmidi * rmidi,int dir)12421761f4ccSTakashi Iwai static void fill_substream_names(struct snd_ump_endpoint *ump,
12431761f4ccSTakashi Iwai 				 struct snd_rawmidi *rmidi, int dir)
12441761f4ccSTakashi Iwai {
12451761f4ccSTakashi Iwai 	struct snd_rawmidi_substream *s;
12468ddb4126STakashi Iwai 	const char *name;
12478ddb4126STakashi Iwai 	int idx;
12481761f4ccSTakashi Iwai 
12498ddb4126STakashi Iwai 	list_for_each_entry(s, &rmidi->streams[dir].substreams, list) {
12508ddb4126STakashi Iwai 		idx = ump->legacy_mapping[s->number];
12518ddb4126STakashi Iwai 		name = ump->groups[idx].name;
12528ddb4126STakashi Iwai 		if (!*name)
12538ddb4126STakashi Iwai 			name = ump->info.name;
1254*d4eb5b3cSTakashi Iwai 		scnprintf(s->name, sizeof(s->name), "Group %d (%.16s)%s",
12559617001aSTakashi Iwai 			  idx + 1, name,
12569617001aSTakashi Iwai 			  ump->groups[idx].active ? "" : " [Inactive]");
12578ddb4126STakashi Iwai 	}
12581761f4ccSTakashi Iwai }
12591761f4ccSTakashi Iwai 
update_legacy_names(struct snd_ump_endpoint * ump)12608d891c86STakashi Iwai static void update_legacy_names(struct snd_ump_endpoint *ump)
12618d891c86STakashi Iwai {
12628d891c86STakashi Iwai 	struct snd_rawmidi *rmidi = ump->legacy_rmidi;
12638d891c86STakashi Iwai 
12648d891c86STakashi Iwai 	fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT);
12658d891c86STakashi Iwai 	fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT);
12668d891c86STakashi Iwai }
12678d891c86STakashi Iwai 
snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint * ump,char * id,int device)12680b5288f5STakashi Iwai int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
12690b5288f5STakashi Iwai 				  char *id, int device)
12700b5288f5STakashi Iwai {
12710b5288f5STakashi Iwai 	struct snd_rawmidi *rmidi;
12720b5288f5STakashi Iwai 	bool input, output;
1273b2bcbd03STakashi Iwai 	int err, num;
12740b5288f5STakashi Iwai 
1275b2bcbd03STakashi Iwai 	ump->out_cvts = kcalloc(SNDRV_UMP_MAX_GROUPS,
1276b2bcbd03STakashi Iwai 				sizeof(*ump->out_cvts), GFP_KERNEL);
127733cd7630STakashi Iwai 	if (!ump->out_cvts)
127833cd7630STakashi Iwai 		return -ENOMEM;
12790b5288f5STakashi Iwai 
1280b2bcbd03STakashi Iwai 	num = fill_legacy_mapping(ump);
1281b2bcbd03STakashi Iwai 
12820b5288f5STakashi Iwai 	input = ump->core.info_flags & SNDRV_RAWMIDI_INFO_INPUT;
12830b5288f5STakashi Iwai 	output = ump->core.info_flags & SNDRV_RAWMIDI_INFO_OUTPUT;
12840b5288f5STakashi Iwai 	err = snd_rawmidi_new(ump->core.card, id, device,
1285b2bcbd03STakashi Iwai 			      output ? num : 0, input ? num : 0,
12860b5288f5STakashi Iwai 			      &rmidi);
12870b5288f5STakashi Iwai 	if (err < 0) {
128833cd7630STakashi Iwai 		kfree(ump->out_cvts);
12890b5288f5STakashi Iwai 		return err;
12900b5288f5STakashi Iwai 	}
12910b5288f5STakashi Iwai 
12920b5288f5STakashi Iwai 	if (input)
12930b5288f5STakashi Iwai 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
12940b5288f5STakashi Iwai 				    &snd_ump_legacy_input_ops);
12950b5288f5STakashi Iwai 	if (output)
12960b5288f5STakashi Iwai 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
12970b5288f5STakashi Iwai 				    &snd_ump_legacy_output_ops);
12984aa69d64STakashi Iwai 	snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)",
12995f11dd93STakashi Iwai 		 ump->info.name);
13000b5288f5STakashi Iwai 	rmidi->info_flags = ump->core.info_flags & ~SNDRV_RAWMIDI_INFO_UMP;
13010b5288f5STakashi Iwai 	rmidi->ops = &snd_ump_legacy_ops;
13020b5288f5STakashi Iwai 	rmidi->private_data = ump;
13030b5288f5STakashi Iwai 	ump->legacy_rmidi = rmidi;
13048d891c86STakashi Iwai 	update_legacy_names(ump);
13051761f4ccSTakashi Iwai 
13060b5288f5STakashi Iwai 	ump_dbg(ump, "Created a legacy rawmidi #%d (%s)\n", device, id);
13070b5288f5STakashi Iwai 	return 0;
13080b5288f5STakashi Iwai }
13090b5288f5STakashi Iwai EXPORT_SYMBOL_GPL(snd_ump_attach_legacy_rawmidi);
13100b5288f5STakashi Iwai #endif /* CONFIG_SND_UMP_LEGACY_RAWMIDI */
13110b5288f5STakashi Iwai 
1312e3a8a5b7STakashi Iwai MODULE_DESCRIPTION("Universal MIDI Packet (UMP) Core Driver");
1313e3a8a5b7STakashi Iwai MODULE_LICENSE("GPL");
1314