at master 40 kB view raw
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");