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