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