File Index
Files and directories
Sign in
FILE

ump.c

File preview with syntax highlighting.

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