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