summaryrefslogtreecommitdiff
path: root/sound/core/seq/oss/seq_oss_midi.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/core/seq/oss/seq_oss_midi.c')
-rw-r--r--sound/core/seq/oss/seq_oss_midi.c710
1 files changed, 710 insertions, 0 deletions
diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c
new file mode 100644
index 000000000000..9aece6c65dbc
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_midi.c
@@ -0,0 +1,710 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * MIDI device handlers
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "seq_oss_midi.h"
+#include "seq_oss_readq.h"
+#include "seq_oss_timer.h"
+#include "seq_oss_event.h"
+#include <sound/seq_midi_event.h>
+#include "../seq_lock.h"
+#include <linux/init.h>
+
+
+/*
+ * constants
+ */
+#define SNDRV_SEQ_OSS_MAX_MIDI_NAME 30
+
+/*
+ * definition of midi device record
+ */
+struct seq_oss_midi_t {
+ int seq_device; /* device number */
+ int client; /* sequencer client number */
+ int port; /* sequencer port number */
+ unsigned int flags; /* port capability */
+ int opened; /* flag for opening */
+ unsigned char name[SNDRV_SEQ_OSS_MAX_MIDI_NAME];
+ snd_midi_event_t *coder; /* MIDI event coder */
+ seq_oss_devinfo_t *devinfo; /* assigned OSSseq device */
+ snd_use_lock_t use_lock;
+};
+
+
+/*
+ * midi device table
+ */
+static int max_midi_devs;
+static seq_oss_midi_t *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS];
+
+static DEFINE_SPINLOCK(register_lock);
+
+/*
+ * prototypes
+ */
+static seq_oss_midi_t *get_mdev(int dev);
+static seq_oss_midi_t *get_mididev(seq_oss_devinfo_t *dp, int dev);
+static int send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev);
+static int send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev);
+
+/*
+ * look up the existing ports
+ * this looks a very exhausting job.
+ */
+int __init
+snd_seq_oss_midi_lookup_ports(int client)
+{
+ snd_seq_client_info_t *clinfo;
+ snd_seq_port_info_t *pinfo;
+
+ clinfo = kcalloc(1, sizeof(*clinfo), GFP_KERNEL);
+ pinfo = kcalloc(1, sizeof(*pinfo), GFP_KERNEL);
+ if (! clinfo || ! pinfo) {
+ kfree(clinfo);
+ kfree(pinfo);
+ return -ENOMEM;
+ }
+ clinfo->client = -1;
+ while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, clinfo) == 0) {
+ if (clinfo->client == client)
+ continue; /* ignore myself */
+ pinfo->addr.client = clinfo->client;
+ pinfo->addr.port = -1;
+ while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, pinfo) == 0)
+ snd_seq_oss_midi_check_new_port(pinfo);
+ }
+ kfree(clinfo);
+ kfree(pinfo);
+ return 0;
+}
+
+
+/*
+ */
+static seq_oss_midi_t *
+get_mdev(int dev)
+{
+ seq_oss_midi_t *mdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&register_lock, flags);
+ mdev = midi_devs[dev];
+ if (mdev)
+ snd_use_lock_use(&mdev->use_lock);
+ spin_unlock_irqrestore(&register_lock, flags);
+ return mdev;
+}
+
+/*
+ * look for the identical slot
+ */
+static seq_oss_midi_t *
+find_slot(int client, int port)
+{
+ int i;
+ seq_oss_midi_t *mdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&register_lock, flags);
+ for (i = 0; i < max_midi_devs; i++) {
+ mdev = midi_devs[i];
+ if (mdev && mdev->client == client && mdev->port == port) {
+ /* found! */
+ snd_use_lock_use(&mdev->use_lock);
+ spin_unlock_irqrestore(&register_lock, flags);
+ return mdev;
+ }
+ }
+ spin_unlock_irqrestore(&register_lock, flags);
+ return NULL;
+}
+
+
+#define PERM_WRITE (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE)
+#define PERM_READ (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ)
+/*
+ * register a new port if it doesn't exist yet
+ */
+int
+snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo)
+{
+ int i;
+ seq_oss_midi_t *mdev;
+ unsigned long flags;
+
+ debug_printk(("check for MIDI client %d port %d\n", pinfo->addr.client, pinfo->addr.port));
+ /* the port must include generic midi */
+ if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC))
+ return 0;
+ /* either read or write subscribable */
+ if ((pinfo->capability & PERM_WRITE) != PERM_WRITE &&
+ (pinfo->capability & PERM_READ) != PERM_READ)
+ return 0;
+
+ /*
+ * look for the identical slot
+ */
+ if ((mdev = find_slot(pinfo->addr.client, pinfo->addr.port)) != NULL) {
+ /* already exists */
+ snd_use_lock_free(&mdev->use_lock);
+ return 0;
+ }
+
+ /*
+ * allocate midi info record
+ */
+ if ((mdev = kcalloc(1, sizeof(*mdev), GFP_KERNEL)) == NULL) {
+ snd_printk(KERN_ERR "can't malloc midi info\n");
+ return -ENOMEM;
+ }
+
+ /* copy the port information */
+ mdev->client = pinfo->addr.client;
+ mdev->port = pinfo->addr.port;
+ mdev->flags = pinfo->capability;
+ mdev->opened = 0;
+ snd_use_lock_init(&mdev->use_lock);
+
+ /* copy and truncate the name of synth device */
+ strlcpy(mdev->name, pinfo->name, sizeof(mdev->name));
+
+ /* create MIDI coder */
+ if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) {
+ snd_printk(KERN_ERR "can't malloc midi coder\n");
+ kfree(mdev);
+ return -ENOMEM;
+ }
+ /* OSS sequencer adds running status to all sequences */
+ snd_midi_event_no_status(mdev->coder, 1);
+
+ /*
+ * look for en empty slot
+ */
+ spin_lock_irqsave(&register_lock, flags);
+ for (i = 0; i < max_midi_devs; i++) {
+ if (midi_devs[i] == NULL)
+ break;
+ }
+ if (i >= max_midi_devs) {
+ if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) {
+ spin_unlock_irqrestore(&register_lock, flags);
+ snd_midi_event_free(mdev->coder);
+ kfree(mdev);
+ return -ENOMEM;
+ }
+ max_midi_devs++;
+ }
+ mdev->seq_device = i;
+ midi_devs[mdev->seq_device] = mdev;
+ spin_unlock_irqrestore(&register_lock, flags);
+
+ return 0;
+}
+
+/*
+ * release the midi device if it was registered
+ */
+int
+snd_seq_oss_midi_check_exit_port(int client, int port)
+{
+ seq_oss_midi_t *mdev;
+ unsigned long flags;
+ int index;
+
+ if ((mdev = find_slot(client, port)) != NULL) {
+ spin_lock_irqsave(&register_lock, flags);
+ midi_devs[mdev->seq_device] = NULL;
+ spin_unlock_irqrestore(&register_lock, flags);
+ snd_use_lock_free(&mdev->use_lock);
+ snd_use_lock_sync(&mdev->use_lock);
+ if (mdev->coder)
+ snd_midi_event_free(mdev->coder);
+ kfree(mdev);
+ }
+ spin_lock_irqsave(&register_lock, flags);
+ for (index = max_midi_devs - 1; index >= 0; index--) {
+ if (midi_devs[index])
+ break;
+ }
+ max_midi_devs = index + 1;
+ spin_unlock_irqrestore(&register_lock, flags);
+ return 0;
+}
+
+
+/*
+ * release the midi device if it was registered
+ */
+void
+snd_seq_oss_midi_clear_all(void)
+{
+ int i;
+ seq_oss_midi_t *mdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&register_lock, flags);
+ for (i = 0; i < max_midi_devs; i++) {
+ if ((mdev = midi_devs[i]) != NULL) {
+ if (mdev->coder)
+ snd_midi_event_free(mdev->coder);
+ kfree(mdev);
+ midi_devs[i] = NULL;
+ }
+ }
+ max_midi_devs = 0;
+ spin_unlock_irqrestore(&register_lock, flags);
+}
+
+
+/*
+ * set up midi tables
+ */
+void
+snd_seq_oss_midi_setup(seq_oss_devinfo_t *dp)
+{
+ dp->max_mididev = max_midi_devs;
+}
+
+/*
+ * clean up midi tables
+ */
+void
+snd_seq_oss_midi_cleanup(seq_oss_devinfo_t *dp)
+{
+ int i;
+ for (i = 0; i < dp->max_mididev; i++)
+ snd_seq_oss_midi_close(dp, i);
+ dp->max_mididev = 0;
+}
+
+
+/*
+ * open all midi devices. ignore errors.
+ */
+void
+snd_seq_oss_midi_open_all(seq_oss_devinfo_t *dp, int file_mode)
+{
+ int i;
+ for (i = 0; i < dp->max_mididev; i++)
+ snd_seq_oss_midi_open(dp, i, file_mode);
+}
+
+
+/*
+ * get the midi device information
+ */
+static seq_oss_midi_t *
+get_mididev(seq_oss_devinfo_t *dp, int dev)
+{
+ if (dev < 0 || dev >= dp->max_mididev)
+ return NULL;
+ return get_mdev(dev);
+}
+
+
+/*
+ * open the midi device if not opened yet
+ */
+int
+snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int fmode)
+{
+ int perm;
+ seq_oss_midi_t *mdev;
+ snd_seq_port_subscribe_t subs;
+
+ if ((mdev = get_mididev(dp, dev)) == NULL)
+ return -ENODEV;
+
+ /* already used? */
+ if (mdev->opened && mdev->devinfo != dp) {
+ snd_use_lock_free(&mdev->use_lock);
+ return -EBUSY;
+ }
+
+ perm = 0;
+ if (is_write_mode(fmode))
+ perm |= PERM_WRITE;
+ if (is_read_mode(fmode))
+ perm |= PERM_READ;
+ perm &= mdev->flags;
+ if (perm == 0) {
+ snd_use_lock_free(&mdev->use_lock);
+ return -ENXIO;
+ }
+
+ /* already opened? */
+ if ((mdev->opened & perm) == perm) {
+ snd_use_lock_free(&mdev->use_lock);
+ return 0;
+ }
+
+ perm &= ~mdev->opened;
+
+ memset(&subs, 0, sizeof(subs));
+
+ if (perm & PERM_WRITE) {
+ subs.sender = dp->addr;
+ subs.dest.client = mdev->client;
+ subs.dest.port = mdev->port;
+ if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
+ mdev->opened |= PERM_WRITE;
+ }
+ if (perm & PERM_READ) {
+ subs.sender.client = mdev->client;
+ subs.sender.port = mdev->port;
+ subs.dest = dp->addr;
+ subs.flags = SNDRV_SEQ_PORT_SUBS_TIMESTAMP;
+ subs.queue = dp->queue; /* queue for timestamps */
+ if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
+ mdev->opened |= PERM_READ;
+ }
+
+ if (! mdev->opened) {
+ snd_use_lock_free(&mdev->use_lock);
+ return -ENXIO;
+ }
+
+ mdev->devinfo = dp;
+ snd_use_lock_free(&mdev->use_lock);
+ return 0;
+}
+
+/*
+ * close the midi device if already opened
+ */
+int
+snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev)
+{
+ seq_oss_midi_t *mdev;
+ snd_seq_port_subscribe_t subs;
+
+ if ((mdev = get_mididev(dp, dev)) == NULL)
+ return -ENODEV;
+ if (! mdev->opened || mdev->devinfo != dp) {
+ snd_use_lock_free(&mdev->use_lock);
+ return 0;
+ }
+
+ debug_printk(("closing client %d port %d mode %d\n", mdev->client, mdev->port, mdev->opened));
+ memset(&subs, 0, sizeof(subs));
+ if (mdev->opened & PERM_WRITE) {
+ subs.sender = dp->addr;
+ subs.dest.client = mdev->client;
+ subs.dest.port = mdev->port;
+ snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs);
+ }
+ if (mdev->opened & PERM_READ) {
+ subs.sender.client = mdev->client;
+ subs.sender.port = mdev->port;
+ subs.dest = dp->addr;
+ snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs);
+ }
+
+ mdev->opened = 0;
+ mdev->devinfo = NULL;
+
+ snd_use_lock_free(&mdev->use_lock);
+ return 0;
+}
+
+/*
+ * change seq capability flags to file mode flags
+ */
+int
+snd_seq_oss_midi_filemode(seq_oss_devinfo_t *dp, int dev)
+{
+ seq_oss_midi_t *mdev;
+ int mode;
+
+ if ((mdev = get_mididev(dp, dev)) == NULL)
+ return 0;
+
+ mode = 0;
+ if (mdev->opened & PERM_WRITE)
+ mode |= SNDRV_SEQ_OSS_FILE_WRITE;
+ if (mdev->opened & PERM_READ)
+ mode |= SNDRV_SEQ_OSS_FILE_READ;
+
+ snd_use_lock_free(&mdev->use_lock);
+ return mode;
+}
+
+/*
+ * reset the midi device and close it:
+ * so far, only close the device.
+ */
+void
+snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev)
+{
+ seq_oss_midi_t *mdev;
+
+ if ((mdev = get_mididev(dp, dev)) == NULL)
+ return;
+ if (! mdev->opened) {
+ snd_use_lock_free(&mdev->use_lock);
+ return;
+ }
+
+ if (mdev->opened & PERM_WRITE) {
+ snd_seq_event_t ev;
+ int c;
+
+ debug_printk(("resetting client %d port %d\n", mdev->client, mdev->port));
+ memset(&ev, 0, sizeof(ev));
+ ev.dest.client = mdev->client;
+ ev.dest.port = mdev->port;
+ ev.queue = dp->queue;
+ ev.source.port = dp->port;
+ if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) {
+ ev.type = SNDRV_SEQ_EVENT_SENSING;
+ snd_seq_oss_dispatch(dp, &ev, 0, 0); /* active sensing */
+ }
+ for (c = 0; c < 16; c++) {
+ ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
+ ev.data.control.channel = c;
+ ev.data.control.param = 123;
+ snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */
+ if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
+ ev.data.control.param = 121;
+ snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */
+ ev.type = SNDRV_SEQ_EVENT_PITCHBEND;
+ ev.data.control.value = 0;
+ snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */
+ }
+ }
+ }
+ // snd_seq_oss_midi_close(dp, dev);
+ snd_use_lock_free(&mdev->use_lock);
+}
+
+
+/*
+ * get client/port of the specified MIDI device
+ */
+void
+snd_seq_oss_midi_get_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_addr_t *addr)
+{
+ seq_oss_midi_t *mdev;
+
+ if ((mdev = get_mididev(dp, dev)) == NULL)
+ return;
+ addr->client = mdev->client;
+ addr->port = mdev->port;
+ snd_use_lock_free(&mdev->use_lock);
+}
+
+
+/*
+ * input callback - this can be atomic
+ */
+int
+snd_seq_oss_midi_input(snd_seq_event_t *ev, int direct, void *private_data)
+{
+ seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private_data;
+ seq_oss_midi_t *mdev;
+ int rc;
+
+ if (dp->readq == NULL)
+ return 0;
+ if ((mdev = find_slot(ev->source.client, ev->source.port)) == NULL)
+ return 0;
+ if (! (mdev->opened & PERM_READ)) {
+ snd_use_lock_free(&mdev->use_lock);
+ return 0;
+ }
+
+ if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
+ rc = send_synth_event(dp, ev, mdev->seq_device);
+ else
+ rc = send_midi_event(dp, ev, mdev);
+
+ snd_use_lock_free(&mdev->use_lock);
+ return rc;
+}
+
+/*
+ * convert ALSA sequencer event to OSS synth event
+ */
+static int
+send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev)
+{
+ evrec_t ossev;
+
+ memset(&ossev, 0, sizeof(ossev));
+
+ switch (ev->type) {
+ case SNDRV_SEQ_EVENT_NOTEON:
+ ossev.v.cmd = MIDI_NOTEON; break;
+ case SNDRV_SEQ_EVENT_NOTEOFF:
+ ossev.v.cmd = MIDI_NOTEOFF; break;
+ case SNDRV_SEQ_EVENT_KEYPRESS:
+ ossev.v.cmd = MIDI_KEY_PRESSURE; break;
+ case SNDRV_SEQ_EVENT_CONTROLLER:
+ ossev.l.cmd = MIDI_CTL_CHANGE; break;
+ case SNDRV_SEQ_EVENT_PGMCHANGE:
+ ossev.l.cmd = MIDI_PGM_CHANGE; break;
+ case SNDRV_SEQ_EVENT_CHANPRESS:
+ ossev.l.cmd = MIDI_CHN_PRESSURE; break;
+ case SNDRV_SEQ_EVENT_PITCHBEND:
+ ossev.l.cmd = MIDI_PITCH_BEND; break;
+ default:
+ return 0; /* not supported */
+ }
+
+ ossev.v.dev = dev;
+
+ switch (ev->type) {
+ case SNDRV_SEQ_EVENT_NOTEON:
+ case SNDRV_SEQ_EVENT_NOTEOFF:
+ case SNDRV_SEQ_EVENT_KEYPRESS:
+ ossev.v.code = EV_CHN_VOICE;
+ ossev.v.note = ev->data.note.note;
+ ossev.v.parm = ev->data.note.velocity;
+ ossev.v.chn = ev->data.note.channel;
+ break;
+ case SNDRV_SEQ_EVENT_CONTROLLER:
+ case SNDRV_SEQ_EVENT_PGMCHANGE:
+ case SNDRV_SEQ_EVENT_CHANPRESS:
+ ossev.l.code = EV_CHN_COMMON;
+ ossev.l.p1 = ev->data.control.param;
+ ossev.l.val = ev->data.control.value;
+ ossev.l.chn = ev->data.control.channel;
+ break;
+ case SNDRV_SEQ_EVENT_PITCHBEND:
+ ossev.l.code = EV_CHN_COMMON;
+ ossev.l.val = ev->data.control.value + 8192;
+ ossev.l.chn = ev->data.control.channel;
+ break;
+ }
+
+ snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode);
+ snd_seq_oss_readq_put_event(dp->readq, &ossev);
+
+ return 0;
+}
+
+/*
+ * decode event and send MIDI bytes to read queue
+ */
+static int
+send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev)
+{
+ char msg[32];
+ int len;
+
+ snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode);
+ if (!dp->timer->running)
+ len = snd_seq_oss_timer_start(dp->timer);
+ if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
+ if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
+ snd_seq_oss_readq_puts(dp->readq, mdev->seq_device,
+ ev->data.ext.ptr, ev->data.ext.len);
+ } else {
+ len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev);
+ if (len > 0)
+ snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, msg, len);
+ }
+
+ return 0;
+}
+
+
+/*
+ * dump midi data
+ * return 0 : enqueued
+ * non-zero : invalid - ignored
+ */
+int
+snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev)
+{
+ seq_oss_midi_t *mdev;
+
+ if ((mdev = get_mididev(dp, dev)) == NULL)
+ return -ENODEV;
+ if (snd_midi_event_encode_byte(mdev->coder, c, ev) > 0) {
+ snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port);
+ snd_use_lock_free(&mdev->use_lock);
+ return 0;
+ }
+ snd_use_lock_free(&mdev->use_lock);
+ return -EINVAL;
+}
+
+/*
+ * create OSS compatible midi_info record
+ */
+int
+snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf)
+{
+ seq_oss_midi_t *mdev;
+
+ if ((mdev = get_mididev(dp, dev)) == NULL)
+ return -ENXIO;
+ inf->device = dev;
+ inf->dev_type = 0; /* FIXME: ?? */
+ inf->capabilities = 0; /* FIXME: ?? */
+ strlcpy(inf->name, mdev->name, sizeof(inf->name));
+ snd_use_lock_free(&mdev->use_lock);
+ return 0;
+}
+
+
+/*
+ * proc interface
+ */
+static char *
+capmode_str(int val)
+{
+ val &= PERM_READ|PERM_WRITE;
+ if (val == (PERM_READ|PERM_WRITE))
+ return "read/write";
+ else if (val == PERM_READ)
+ return "read";
+ else if (val == PERM_WRITE)
+ return "write";
+ else
+ return "none";
+}
+
+void
+snd_seq_oss_midi_info_read(snd_info_buffer_t *buf)
+{
+ int i;
+ seq_oss_midi_t *mdev;
+
+ snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs);
+ for (i = 0; i < max_midi_devs; i++) {
+ snd_iprintf(buf, "\nmidi %d: ", i);
+ mdev = get_mdev(i);
+ if (mdev == NULL) {
+ snd_iprintf(buf, "*empty*\n");
+ continue;
+ }
+ snd_iprintf(buf, "[%s] ALSA port %d:%d\n", mdev->name,
+ mdev->client, mdev->port);
+ snd_iprintf(buf, " capability %s / opened %s\n",
+ capmode_str(mdev->flags),
+ capmode_str(mdev->opened));
+ snd_use_lock_free(&mdev->use_lock);
+ }
+}
+