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