1/* 2 * OSS compatible sequencer driver 3 * 4 * MIDI device handlers 5 * 6 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23#include <sound/asoundef.h> 24#include "seq_oss_midi.h" 25#include "seq_oss_readq.h" 26#include "seq_oss_timer.h" 27#include "seq_oss_event.h" 28#include <sound/seq_midi_event.h> 29#include "../seq_lock.h" 30#include <linux/init.h> 31#include <linux/slab.h> 32 33 34/* 35 * constants 36 */ 37#define SNDRV_SEQ_OSS_MAX_MIDI_NAME 30 38 39/* 40 * definition of midi device record 41 */ 42struct seq_oss_midi { 43 int seq_device; /* device number */ 44 int client; /* sequencer client number */ 45 int port; /* sequencer port number */ 46 unsigned int flags; /* port capability */ 47 int opened; /* flag for opening */ 48 unsigned char name[SNDRV_SEQ_OSS_MAX_MIDI_NAME]; 49 struct snd_midi_event *coder; /* MIDI event coder */ 50 struct seq_oss_devinfo *devinfo; /* assigned OSSseq device */ 51 snd_use_lock_t use_lock; 52}; 53 54 55/* 56 * midi device table 57 */ 58static int max_midi_devs; 59static struct seq_oss_midi *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS]; 60 61static DEFINE_SPINLOCK(register_lock); 62 63/* 64 * prototypes 65 */ 66static struct seq_oss_midi *get_mdev(int dev); 67static struct seq_oss_midi *get_mididev(struct seq_oss_devinfo *dp, int dev); 68static int send_synth_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int dev); 69static int send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq_oss_midi *mdev); 70 71/* 72 * look up the existing ports 73 * this looks a very exhausting job. 74 */ 75int __init 76snd_seq_oss_midi_lookup_ports(int client) 77{ 78 struct snd_seq_client_info *clinfo; 79 struct snd_seq_port_info *pinfo; 80 81 clinfo = kzalloc(sizeof(*clinfo), GFP_KERNEL); 82 pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL); 83 if (! clinfo || ! pinfo) { 84 kfree(clinfo); 85 kfree(pinfo); 86 return -ENOMEM; 87 } 88 clinfo->client = -1; 89 while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, clinfo) == 0) { 90 if (clinfo->client == client) 91 continue; /* ignore myself */ 92 pinfo->addr.client = clinfo->client; 93 pinfo->addr.port = -1; 94 while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, pinfo) == 0) 95 snd_seq_oss_midi_check_new_port(pinfo); 96 } 97 kfree(clinfo); 98 kfree(pinfo); 99 return 0; 100} 101 102 103/* 104 */ 105static struct seq_oss_midi * 106get_mdev(int dev) 107{ 108 struct seq_oss_midi *mdev; 109 unsigned long flags; 110 111 spin_lock_irqsave(®ister_lock, flags); 112 mdev = midi_devs[dev]; 113 if (mdev) 114 snd_use_lock_use(&mdev->use_lock); 115 spin_unlock_irqrestore(®ister_lock, flags); 116 return mdev; 117} 118 119/* 120 * look for the identical slot 121 */ 122static struct seq_oss_midi * 123find_slot(int client, int port) 124{ 125 int i; 126 struct seq_oss_midi *mdev; 127 unsigned long flags; 128 129 spin_lock_irqsave(®ister_lock, flags); 130 for (i = 0; i < max_midi_devs; i++) { 131 mdev = midi_devs[i]; 132 if (mdev && mdev->client == client && mdev->port == port) { 133 /* found! */ 134 snd_use_lock_use(&mdev->use_lock); 135 spin_unlock_irqrestore(®ister_lock, flags); 136 return mdev; 137 } 138 } 139 spin_unlock_irqrestore(®ister_lock, flags); 140 return NULL; 141} 142 143 144#define PERM_WRITE (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE) 145#define PERM_READ (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ) 146/* 147 * register a new port if it doesn't exist yet 148 */ 149int 150snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo) 151{ 152 int i; 153 struct seq_oss_midi *mdev; 154 unsigned long flags; 155 156 debug_printk(("check for MIDI client %d port %d\n", pinfo->addr.client, pinfo->addr.port)); 157 /* the port must include generic midi */ 158 if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC)) 159 return 0; 160 /* either read or write subscribable */ 161 if ((pinfo->capability & PERM_WRITE) != PERM_WRITE && 162 (pinfo->capability & PERM_READ) != PERM_READ) 163 return 0; 164 165 /* 166 * look for the identical slot 167 */ 168 if ((mdev = find_slot(pinfo->addr.client, pinfo->addr.port)) != NULL) { 169 /* already exists */ 170 snd_use_lock_free(&mdev->use_lock); 171 return 0; 172 } 173 174 /* 175 * allocate midi info record 176 */ 177 if ((mdev = kzalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) { 178 snd_printk(KERN_ERR "can't malloc midi info\n"); 179 return -ENOMEM; 180 } 181 182 /* copy the port information */ 183 mdev->client = pinfo->addr.client; 184 mdev->port = pinfo->addr.port; 185 mdev->flags = pinfo->capability; 186 mdev->opened = 0; 187 snd_use_lock_init(&mdev->use_lock); 188 189 /* copy and truncate the name of synth device */ 190 strlcpy(mdev->name, pinfo->name, sizeof(mdev->name)); 191 192 /* create MIDI coder */ 193 if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) { 194 snd_printk(KERN_ERR "can't malloc midi coder\n"); 195 kfree(mdev); 196 return -ENOMEM; 197 } 198 /* OSS sequencer adds running status to all sequences */ 199 snd_midi_event_no_status(mdev->coder, 1); 200 201 /* 202 * look for en empty slot 203 */ 204 spin_lock_irqsave(®ister_lock, flags); 205 for (i = 0; i < max_midi_devs; i++) { 206 if (midi_devs[i] == NULL) 207 break; 208 } 209 if (i >= max_midi_devs) { 210 if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) { 211 spin_unlock_irqrestore(®ister_lock, flags); 212 snd_midi_event_free(mdev->coder); 213 kfree(mdev); 214 return -ENOMEM; 215 } 216 max_midi_devs++; 217 } 218 mdev->seq_device = i; 219 midi_devs[mdev->seq_device] = mdev; 220 spin_unlock_irqrestore(®ister_lock, flags); 221 222 return 0; 223} 224 225/* 226 * release the midi device if it was registered 227 */ 228int 229snd_seq_oss_midi_check_exit_port(int client, int port) 230{ 231 struct seq_oss_midi *mdev; 232 unsigned long flags; 233 int index; 234 235 if ((mdev = find_slot(client, port)) != NULL) { 236 spin_lock_irqsave(®ister_lock, flags); 237 midi_devs[mdev->seq_device] = NULL; 238 spin_unlock_irqrestore(®ister_lock, flags); 239 snd_use_lock_free(&mdev->use_lock); 240 snd_use_lock_sync(&mdev->use_lock); 241 if (mdev->coder) 242 snd_midi_event_free(mdev->coder); 243 kfree(mdev); 244 } 245 spin_lock_irqsave(®ister_lock, flags); 246 for (index = max_midi_devs - 1; index >= 0; index--) { 247 if (midi_devs[index]) 248 break; 249 } 250 max_midi_devs = index + 1; 251 spin_unlock_irqrestore(®ister_lock, flags); 252 return 0; 253} 254 255 256/* 257 * release the midi device if it was registered 258 */ 259void 260snd_seq_oss_midi_clear_all(void) 261{ 262 int i; 263 struct seq_oss_midi *mdev; 264 unsigned long flags; 265 266 spin_lock_irqsave(®ister_lock, flags); 267 for (i = 0; i < max_midi_devs; i++) { 268 if ((mdev = midi_devs[i]) != NULL) { 269 if (mdev->coder) 270 snd_midi_event_free(mdev->coder); 271 kfree(mdev); 272 midi_devs[i] = NULL; 273 } 274 } 275 max_midi_devs = 0; 276 spin_unlock_irqrestore(®ister_lock, flags); 277} 278 279 280/* 281 * set up midi tables 282 */ 283void 284snd_seq_oss_midi_setup(struct seq_oss_devinfo *dp) 285{ 286 dp->max_mididev = max_midi_devs; 287} 288 289/* 290 * clean up midi tables 291 */ 292void 293snd_seq_oss_midi_cleanup(struct seq_oss_devinfo *dp) 294{ 295 int i; 296 for (i = 0; i < dp->max_mididev; i++) 297 snd_seq_oss_midi_close(dp, i); 298 dp->max_mididev = 0; 299} 300 301 302/* 303 * open all midi devices. ignore errors. 304 */ 305void 306snd_seq_oss_midi_open_all(struct seq_oss_devinfo *dp, int file_mode) 307{ 308 int i; 309 for (i = 0; i < dp->max_mididev; i++) 310 snd_seq_oss_midi_open(dp, i, file_mode); 311} 312 313 314/* 315 * get the midi device information 316 */ 317static struct seq_oss_midi * 318get_mididev(struct seq_oss_devinfo *dp, int dev) 319{ 320 if (dev < 0 || dev >= dp->max_mididev) 321 return NULL; 322 return get_mdev(dev); 323} 324 325 326/* 327 * open the midi device if not opened yet 328 */ 329int 330snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode) 331{ 332 int perm; 333 struct seq_oss_midi *mdev; 334 struct snd_seq_port_subscribe subs; 335 336 if ((mdev = get_mididev(dp, dev)) == NULL) 337 return -ENODEV; 338 339 /* already used? */ 340 if (mdev->opened && mdev->devinfo != dp) { 341 snd_use_lock_free(&mdev->use_lock); 342 return -EBUSY; 343 } 344 345 perm = 0; 346 if (is_write_mode(fmode)) 347 perm |= PERM_WRITE; 348 if (is_read_mode(fmode)) 349 perm |= PERM_READ; 350 perm &= mdev->flags; 351 if (perm == 0) { 352 snd_use_lock_free(&mdev->use_lock); 353 return -ENXIO; 354 } 355 356 /* already opened? */ 357 if ((mdev->opened & perm) == perm) { 358 snd_use_lock_free(&mdev->use_lock); 359 return 0; 360 } 361 362 perm &= ~mdev->opened; 363 364 memset(&subs, 0, sizeof(subs)); 365 366 if (perm & PERM_WRITE) { 367 subs.sender = dp->addr; 368 subs.dest.client = mdev->client; 369 subs.dest.port = mdev->port; 370 if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0) 371 mdev->opened |= PERM_WRITE; 372 } 373 if (perm & PERM_READ) { 374 subs.sender.client = mdev->client; 375 subs.sender.port = mdev->port; 376 subs.dest = dp->addr; 377 subs.flags = SNDRV_SEQ_PORT_SUBS_TIMESTAMP; 378 subs.queue = dp->queue; /* queue for timestamps */ 379 if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0) 380 mdev->opened |= PERM_READ; 381 } 382 383 if (! mdev->opened) { 384 snd_use_lock_free(&mdev->use_lock); 385 return -ENXIO; 386 } 387 388 mdev->devinfo = dp; 389 snd_use_lock_free(&mdev->use_lock); 390 return 0; 391} 392 393/* 394 * close the midi device if already opened 395 */ 396int 397snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev) 398{ 399 struct seq_oss_midi *mdev; 400 struct snd_seq_port_subscribe subs; 401 402 if ((mdev = get_mididev(dp, dev)) == NULL) 403 return -ENODEV; 404 if (! mdev->opened || mdev->devinfo != dp) { 405 snd_use_lock_free(&mdev->use_lock); 406 return 0; 407 } 408 409 debug_printk(("closing client %d port %d mode %d\n", mdev->client, mdev->port, mdev->opened)); 410 memset(&subs, 0, sizeof(subs)); 411 if (mdev->opened & PERM_WRITE) { 412 subs.sender = dp->addr; 413 subs.dest.client = mdev->client; 414 subs.dest.port = mdev->port; 415 snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs); 416 } 417 if (mdev->opened & PERM_READ) { 418 subs.sender.client = mdev->client; 419 subs.sender.port = mdev->port; 420 subs.dest = dp->addr; 421 snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs); 422 } 423 424 mdev->opened = 0; 425 mdev->devinfo = NULL; 426 427 snd_use_lock_free(&mdev->use_lock); 428 return 0; 429} 430 431/* 432 * change seq capability flags to file mode flags 433 */ 434int 435snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev) 436{ 437 struct seq_oss_midi *mdev; 438 int mode; 439 440 if ((mdev = get_mididev(dp, dev)) == NULL) 441 return 0; 442 443 mode = 0; 444 if (mdev->opened & PERM_WRITE) 445 mode |= SNDRV_SEQ_OSS_FILE_WRITE; 446 if (mdev->opened & PERM_READ) 447 mode |= SNDRV_SEQ_OSS_FILE_READ; 448 449 snd_use_lock_free(&mdev->use_lock); 450 return mode; 451} 452 453/* 454 * reset the midi device and close it: 455 * so far, only close the device. 456 */ 457void 458snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev) 459{ 460 struct seq_oss_midi *mdev; 461 462 if ((mdev = get_mididev(dp, dev)) == NULL) 463 return; 464 if (! mdev->opened) { 465 snd_use_lock_free(&mdev->use_lock); 466 return; 467 } 468 469 if (mdev->opened & PERM_WRITE) { 470 struct snd_seq_event ev; 471 int c; 472 473 debug_printk(("resetting client %d port %d\n", mdev->client, mdev->port)); 474 memset(&ev, 0, sizeof(ev)); 475 ev.dest.client = mdev->client; 476 ev.dest.port = mdev->port; 477 ev.queue = dp->queue; 478 ev.source.port = dp->port; 479 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) { 480 ev.type = SNDRV_SEQ_EVENT_SENSING; 481 snd_seq_oss_dispatch(dp, &ev, 0, 0); 482 } 483 for (c = 0; c < 16; c++) { 484 ev.type = SNDRV_SEQ_EVENT_CONTROLLER; 485 ev.data.control.channel = c; 486 ev.data.control.param = MIDI_CTL_ALL_NOTES_OFF; 487 snd_seq_oss_dispatch(dp, &ev, 0, 0); 488 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) { 489 ev.data.control.param = 490 MIDI_CTL_RESET_CONTROLLERS; 491 snd_seq_oss_dispatch(dp, &ev, 0, 0); 492 ev.type = SNDRV_SEQ_EVENT_PITCHBEND; 493 ev.data.control.value = 0; 494 snd_seq_oss_dispatch(dp, &ev, 0, 0); 495 } 496 } 497 } 498 // snd_seq_oss_midi_close(dp, dev); 499 snd_use_lock_free(&mdev->use_lock); 500} 501 502 503/* 504 * get client/port of the specified MIDI device 505 */ 506void 507snd_seq_oss_midi_get_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_addr *addr) 508{ 509 struct seq_oss_midi *mdev; 510 511 if ((mdev = get_mididev(dp, dev)) == NULL) 512 return; 513 addr->client = mdev->client; 514 addr->port = mdev->port; 515 snd_use_lock_free(&mdev->use_lock); 516} 517 518 519/* 520 * input callback - this can be atomic 521 */ 522int 523snd_seq_oss_midi_input(struct snd_seq_event *ev, int direct, void *private_data) 524{ 525 struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private_data; 526 struct seq_oss_midi *mdev; 527 int rc; 528 529 if (dp->readq == NULL) 530 return 0; 531 if ((mdev = find_slot(ev->source.client, ev->source.port)) == NULL) 532 return 0; 533 if (! (mdev->opened & PERM_READ)) { 534 snd_use_lock_free(&mdev->use_lock); 535 return 0; 536 } 537 538 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) 539 rc = send_synth_event(dp, ev, mdev->seq_device); 540 else 541 rc = send_midi_event(dp, ev, mdev); 542 543 snd_use_lock_free(&mdev->use_lock); 544 return rc; 545} 546 547/* 548 * convert ALSA sequencer event to OSS synth event 549 */ 550static int 551send_synth_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int dev) 552{ 553 union evrec ossev; 554 555 memset(&ossev, 0, sizeof(ossev)); 556 557 switch (ev->type) { 558 case SNDRV_SEQ_EVENT_NOTEON: 559 ossev.v.cmd = MIDI_NOTEON; break; 560 case SNDRV_SEQ_EVENT_NOTEOFF: 561 ossev.v.cmd = MIDI_NOTEOFF; break; 562 case SNDRV_SEQ_EVENT_KEYPRESS: 563 ossev.v.cmd = MIDI_KEY_PRESSURE; break; 564 case SNDRV_SEQ_EVENT_CONTROLLER: 565 ossev.l.cmd = MIDI_CTL_CHANGE; break; 566 case SNDRV_SEQ_EVENT_PGMCHANGE: 567 ossev.l.cmd = MIDI_PGM_CHANGE; break; 568 case SNDRV_SEQ_EVENT_CHANPRESS: 569 ossev.l.cmd = MIDI_CHN_PRESSURE; break; 570 case SNDRV_SEQ_EVENT_PITCHBEND: 571 ossev.l.cmd = MIDI_PITCH_BEND; break; 572 default: 573 return 0; /* not supported */ 574 } 575 576 ossev.v.dev = dev; 577 578 switch (ev->type) { 579 case SNDRV_SEQ_EVENT_NOTEON: 580 case SNDRV_SEQ_EVENT_NOTEOFF: 581 case SNDRV_SEQ_EVENT_KEYPRESS: 582 ossev.v.code = EV_CHN_VOICE; 583 ossev.v.note = ev->data.note.note; 584 ossev.v.parm = ev->data.note.velocity; 585 ossev.v.chn = ev->data.note.channel; 586 break; 587 case SNDRV_SEQ_EVENT_CONTROLLER: 588 case SNDRV_SEQ_EVENT_PGMCHANGE: 589 case SNDRV_SEQ_EVENT_CHANPRESS: 590 ossev.l.code = EV_CHN_COMMON; 591 ossev.l.p1 = ev->data.control.param; 592 ossev.l.val = ev->data.control.value; 593 ossev.l.chn = ev->data.control.channel; 594 break; 595 case SNDRV_SEQ_EVENT_PITCHBEND: 596 ossev.l.code = EV_CHN_COMMON; 597 ossev.l.val = ev->data.control.value + 8192; 598 ossev.l.chn = ev->data.control.channel; 599 break; 600 } 601 602 snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode); 603 snd_seq_oss_readq_put_event(dp->readq, &ossev); 604 605 return 0; 606} 607 608/* 609 * decode event and send MIDI bytes to read queue 610 */ 611static int 612send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq_oss_midi *mdev) 613{ 614 char msg[32]; 615 int len; 616 617 snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode); 618 if (!dp->timer->running) 619 len = snd_seq_oss_timer_start(dp->timer); 620 if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { 621 if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) 622 snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, 623 ev->data.ext.ptr, ev->data.ext.len); 624 } else { 625 len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev); 626 if (len > 0) 627 snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, msg, len); 628 } 629 630 return 0; 631} 632 633 634/* 635 * dump midi data 636 * return 0 : enqueued 637 * non-zero : invalid - ignored 638 */ 639int 640snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, struct snd_seq_event *ev) 641{ 642 struct seq_oss_midi *mdev; 643 644 if ((mdev = get_mididev(dp, dev)) == NULL) 645 return -ENODEV; 646 if (snd_midi_event_encode_byte(mdev->coder, c, ev) > 0) { 647 snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port); 648 snd_use_lock_free(&mdev->use_lock); 649 return 0; 650 } 651 snd_use_lock_free(&mdev->use_lock); 652 return -EINVAL; 653} 654 655/* 656 * create OSS compatible midi_info record 657 */ 658int 659snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info *inf) 660{ 661 struct seq_oss_midi *mdev; 662 663 if ((mdev = get_mididev(dp, dev)) == NULL) 664 return -ENXIO; 665 inf->device = dev; 666 inf->dev_type = 0; 667 inf->capabilities = 0; 668 strlcpy(inf->name, mdev->name, sizeof(inf->name)); 669 snd_use_lock_free(&mdev->use_lock); 670 return 0; 671} 672 673 674#ifdef CONFIG_PROC_FS 675/* 676 * proc interface 677 */ 678static char * 679capmode_str(int val) 680{ 681 val &= PERM_READ|PERM_WRITE; 682 if (val == (PERM_READ|PERM_WRITE)) 683 return "read/write"; 684 else if (val == PERM_READ) 685 return "read"; 686 else if (val == PERM_WRITE) 687 return "write"; 688 else 689 return "none"; 690} 691 692void 693snd_seq_oss_midi_info_read(struct snd_info_buffer *buf) 694{ 695 int i; 696 struct seq_oss_midi *mdev; 697 698 snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs); 699 for (i = 0; i < max_midi_devs; i++) { 700 snd_iprintf(buf, "\nmidi %d: ", i); 701 mdev = get_mdev(i); 702 if (mdev == NULL) { 703 snd_iprintf(buf, "*empty*\n"); 704 continue; 705 } 706 snd_iprintf(buf, "[%s] ALSA port %d:%d\n", mdev->name, 707 mdev->client, mdev->port); 708 snd_iprintf(buf, " capability %s / opened %s\n", 709 capmode_str(mdev->flags), 710 capmode_str(mdev->opened)); 711 snd_use_lock_free(&mdev->use_lock); 712 } 713} 714#endif /* CONFIG_PROC_FS */ 715