ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-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 "seq_oss_midi.h"
24 #include "seq_oss_readq.h"
25 #include "seq_oss_timer.h"
26 #include "seq_oss_event.h"
27 #include <sound/seq_midi_event.h>
28 #include "../seq_lock.h"
29 #include <linux/init.h>
30
31
32 /*
33  * constants
34  */
35 #define SNDRV_SEQ_OSS_MAX_MIDI_NAME     30
36
37 /*
38  * definition of midi device record
39  */
40 struct seq_oss_midi_t {
41         int seq_device;         /* device number */
42         int client;             /* sequencer client number */
43         int port;               /* sequencer port number */
44         unsigned int flags;     /* port capability */
45         int opened;             /* flag for opening */
46         unsigned char name[SNDRV_SEQ_OSS_MAX_MIDI_NAME];
47         snd_midi_event_t *coder;        /* MIDI event coder */
48         seq_oss_devinfo_t *devinfo;     /* assigned OSSseq device */
49         snd_use_lock_t use_lock;
50 };
51
52
53 /*
54  * midi device table
55  */
56 static int max_midi_devs;
57 static seq_oss_midi_t *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS];
58
59 static spinlock_t register_lock = SPIN_LOCK_UNLOCKED;
60
61 /*
62  * prototypes
63  */
64 static seq_oss_midi_t *get_mdev(int dev);
65 static seq_oss_midi_t *get_mididev(seq_oss_devinfo_t *dp, int dev);
66 static int send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev);
67 static int send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev);
68
69 /*
70  * look up the existing ports
71  * this looks a very exhausting job.
72  */
73 int __init
74 snd_seq_oss_midi_lookup_ports(int client)
75 {
76         snd_seq_system_info_t sysinfo;
77         snd_seq_client_info_t clinfo;
78         snd_seq_port_info_t pinfo;
79         int rc;
80
81         rc = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SYSTEM_INFO, &sysinfo);
82         if (rc < 0)
83                 return rc;
84         
85         memset(&clinfo, 0, sizeof(clinfo));
86         memset(&pinfo, 0, sizeof(pinfo));
87         clinfo.client = -1;
88         while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, &clinfo) == 0) {
89                 if (clinfo.client == client)
90                         continue; /* ignore myself */
91                 pinfo.addr.client = clinfo.client;
92                 pinfo.addr.port = -1;
93                 while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, &pinfo) == 0)
94                         snd_seq_oss_midi_check_new_port(&pinfo);
95         }
96         return 0;
97 }
98
99
100 /*
101  */
102 static seq_oss_midi_t *
103 get_mdev(int dev)
104 {
105         seq_oss_midi_t *mdev;
106         unsigned long flags;
107
108         spin_lock_irqsave(&register_lock, flags);
109         mdev = midi_devs[dev];
110         if (mdev)
111                 snd_use_lock_use(&mdev->use_lock);
112         spin_unlock_irqrestore(&register_lock, flags);
113         return mdev;
114 }
115
116 /*
117  * look for the identical slot
118  */
119 static seq_oss_midi_t *
120 find_slot(int client, int port)
121 {
122         int i;
123         seq_oss_midi_t *mdev;
124         unsigned long flags;
125
126         spin_lock_irqsave(&register_lock, flags);
127         for (i = 0; i < max_midi_devs; i++) {
128                 mdev = midi_devs[i];
129                 if (mdev && mdev->client == client && mdev->port == port) {
130                         /* found! */
131                         snd_use_lock_use(&mdev->use_lock);
132                         spin_unlock_irqrestore(&register_lock, flags);
133                         return mdev;
134                 }
135         }
136         spin_unlock_irqrestore(&register_lock, flags);
137         return NULL;
138 }
139
140
141 #define PERM_WRITE (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE)
142 #define PERM_READ (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ)
143 /*
144  * register a new port if it doesn't exist yet
145  */
146 int
147 snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo)
148 {
149         int i;
150         seq_oss_midi_t *mdev;
151         unsigned long flags;
152
153         debug_printk(("check for MIDI client %d port %d\n", pinfo->addr.client, pinfo->addr.port));
154         /* the port must include generic midi */
155         if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC))
156                 return 0;
157         /* either read or write subscribable */
158         if ((pinfo->capability & PERM_WRITE) != PERM_WRITE &&
159             (pinfo->capability & PERM_READ) != PERM_READ)
160                 return 0;
161
162         /*
163          * look for the identical slot
164          */
165         if ((mdev = find_slot(pinfo->addr.client, pinfo->addr.port)) != NULL) {
166                 /* already exists */
167                 snd_use_lock_free(&mdev->use_lock);
168                 return 0;
169         }
170
171         /*
172          * allocate midi info record
173          */
174         if ((mdev = snd_kcalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) {
175                 snd_printk(KERN_ERR "can't malloc midi info\n");
176                 return -ENOMEM;
177         }
178
179         /* copy the port information */
180         mdev->client = pinfo->addr.client;
181         mdev->port = pinfo->addr.port;
182         mdev->flags = pinfo->capability;
183         mdev->opened = 0;
184         snd_use_lock_init(&mdev->use_lock);
185
186         /* copy and truncate the name of synth device */
187         strlcpy(mdev->name, pinfo->name, sizeof(mdev->name));
188
189         /* create MIDI coder */
190         if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) {
191                 snd_printk(KERN_ERR "can't malloc midi coder\n");
192                 kfree(mdev);
193                 return -ENOMEM;
194         }
195         /* OSS sequencer adds running status to all sequences */
196         snd_midi_event_no_status(mdev->coder, 1);
197
198         /*
199          * look for en empty slot
200          */
201         spin_lock_irqsave(&register_lock, flags);
202         for (i = 0; i < max_midi_devs; i++) {
203                 if (midi_devs[i] == NULL)
204                         break;
205         }
206         if (i >= max_midi_devs) {
207                 if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) {
208                         spin_unlock_irqrestore(&register_lock, flags);
209                         snd_midi_event_free(mdev->coder);
210                         kfree(mdev);
211                         return -ENOMEM;
212                 }
213                 max_midi_devs++;
214         }
215         mdev->seq_device = i;
216         midi_devs[mdev->seq_device] = mdev;
217         spin_unlock_irqrestore(&register_lock, flags);
218
219         return 0;
220 }
221
222 /*
223  * release the midi device if it was registered
224  */
225 int
226 snd_seq_oss_midi_check_exit_port(int client, int port)
227 {
228         seq_oss_midi_t *mdev;
229         unsigned long flags;
230         int index;
231
232         if ((mdev = find_slot(client, port)) != NULL) {
233                 spin_lock_irqsave(&register_lock, flags);
234                 midi_devs[mdev->seq_device] = NULL;
235                 spin_unlock_irqrestore(&register_lock, flags);
236                 snd_use_lock_free(&mdev->use_lock);
237                 snd_use_lock_sync(&mdev->use_lock);
238                 if (mdev->coder)
239                         snd_midi_event_free(mdev->coder);
240                 kfree(mdev);
241         }
242         spin_lock_irqsave(&register_lock, flags);
243         for (index = max_midi_devs - 1; index >= 0; index--) {
244                 if (midi_devs[index])
245                         break;
246         }
247         max_midi_devs = index + 1;
248         spin_unlock_irqrestore(&register_lock, flags);
249         return 0;
250 }
251
252
253 /*
254  * release the midi device if it was registered
255  */
256 void
257 snd_seq_oss_midi_clear_all(void)
258 {
259         int i;
260         seq_oss_midi_t *mdev;
261         unsigned long flags;
262
263         spin_lock_irqsave(&register_lock, flags);
264         for (i = 0; i < max_midi_devs; i++) {
265                 if ((mdev = midi_devs[i]) != NULL) {
266                         if (mdev->coder)
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(seq_oss_devinfo_t *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(seq_oss_devinfo_t *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(seq_oss_devinfo_t *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 seq_oss_midi_t *
315 get_mididev(seq_oss_devinfo_t *dp, int dev)
316 {
317         if (dev < 0 || dev >= dp->max_mididev)
318                 return NULL;
319         return get_mdev(dev);
320 }
321
322
323 /*
324  * open the midi device if not opened yet
325  */
326 int
327 snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int fmode)
328 {
329         int perm;
330         seq_oss_midi_t *mdev;
331         snd_seq_port_subscribe_t subs;
332
333         if ((mdev = get_mididev(dp, dev)) == NULL)
334                 return -ENODEV;
335
336         /* already used? */
337         if (mdev->opened && mdev->devinfo != dp) {
338                 snd_use_lock_free(&mdev->use_lock);
339                 return -EBUSY;
340         }
341
342         perm = 0;
343         if (is_write_mode(fmode))
344                 perm |= PERM_WRITE;
345         if (is_read_mode(fmode))
346                 perm |= PERM_READ;
347         perm &= mdev->flags;
348         if (perm == 0) {
349                 snd_use_lock_free(&mdev->use_lock);
350                 return -ENXIO;
351         }
352
353         /* already opened? */
354         if ((mdev->opened & perm) == perm) {
355                 snd_use_lock_free(&mdev->use_lock);
356                 return 0;
357         }
358
359         perm &= ~mdev->opened;
360
361         memset(&subs, 0, sizeof(subs));
362
363         if (perm & PERM_WRITE) {
364                 subs.sender = dp->addr;
365                 subs.dest.client = mdev->client;
366                 subs.dest.port = mdev->port;
367                 if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
368                         mdev->opened |= PERM_WRITE;
369         }
370         if (perm & PERM_READ) {
371                 subs.sender.client = mdev->client;
372                 subs.sender.port = mdev->port;
373                 subs.dest = dp->addr;
374                 subs.flags = SNDRV_SEQ_PORT_SUBS_TIMESTAMP;
375                 subs.queue = dp->queue;         /* queue for timestamps */
376                 if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
377                         mdev->opened |= PERM_READ;
378         }
379
380         if (! mdev->opened) {
381                 snd_use_lock_free(&mdev->use_lock);
382                 return -ENXIO;
383         }
384
385         mdev->devinfo = dp;
386         snd_use_lock_free(&mdev->use_lock);
387         return 0;
388 }
389
390 /*
391  * close the midi device if already opened
392  */
393 int
394 snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev)
395 {
396         seq_oss_midi_t *mdev;
397         snd_seq_port_subscribe_t subs;
398
399         if ((mdev = get_mididev(dp, dev)) == NULL)
400                 return -ENODEV;
401         if (! mdev->opened || mdev->devinfo != dp) {
402                 snd_use_lock_free(&mdev->use_lock);
403                 return 0;
404         }
405
406         debug_printk(("closing client %d port %d mode %d\n", mdev->client, mdev->port, mdev->opened));
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(seq_oss_devinfo_t *dp, int dev)
433 {
434         seq_oss_midi_t *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(seq_oss_devinfo_t *dp, int dev)
456 {
457         seq_oss_midi_t *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                 snd_seq_event_t ev;
468                 int c;
469
470                 debug_printk(("resetting client %d port %d\n", mdev->client, mdev->port));
471                 memset(&ev, 0, sizeof(ev));
472                 ev.dest.client = mdev->client;
473                 ev.dest.port = mdev->port;
474                 ev.queue = dp->queue;
475                 ev.source.port = dp->port;
476                 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) {
477                         ev.type = SNDRV_SEQ_EVENT_SENSING;
478                         snd_seq_oss_dispatch(dp, &ev, 0, 0); /* active sensing */
479                 }
480                 for (c = 0; c < 16; c++) {
481                         ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
482                         ev.data.control.channel = c;
483                         ev.data.control.param = 123;
484                         snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */
485                         if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
486                                 ev.data.control.param = 121;
487                                 snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */
488                                 ev.type = SNDRV_SEQ_EVENT_PITCHBEND;
489                                 ev.data.control.value = 0;
490                                 snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */
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(seq_oss_devinfo_t *dp, int dev, snd_seq_addr_t *addr)
504 {
505         seq_oss_midi_t *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(snd_seq_event_t *ev, int direct, void *private_data)
520 {
521         seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private_data;
522         seq_oss_midi_t *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(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev)
548 {
549         evrec_t 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(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *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                 if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
618                         snd_seq_oss_readq_puts(dp->readq, mdev->seq_device,
619                                                ev->data.ext.ptr, ev->data.ext.len);
620         } else {
621                 len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev);
622                 if (len > 0)
623                         snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, msg, len);
624         }
625
626         return 0;
627 }
628
629
630 /*
631  * dump midi data
632  * return 0 : enqueued
633  *        non-zero : invalid - ignored
634  */
635 int
636 snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev)
637 {
638         seq_oss_midi_t *mdev;
639
640         if ((mdev = get_mididev(dp, dev)) == NULL)
641                 return -ENODEV;
642         if (snd_midi_event_encode_byte(mdev->coder, c, ev) > 0) {
643                 snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port);
644                 snd_use_lock_free(&mdev->use_lock);
645                 return 0;
646         }
647         snd_use_lock_free(&mdev->use_lock);
648         return -EINVAL;
649 }
650
651 /*
652  * create OSS compatible midi_info record
653  */
654 int
655 snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf)
656 {
657         seq_oss_midi_t *mdev;
658
659         if ((mdev = get_mididev(dp, dev)) == NULL)
660                 return -ENXIO;
661         inf->device = dev;
662         inf->dev_type = 0; /* FIXME: ?? */
663         inf->capabilities = 0; /* FIXME: ?? */
664         strlcpy(inf->name, mdev->name, sizeof(inf->name));
665         snd_use_lock_free(&mdev->use_lock);
666         return 0;
667 }
668
669
670 /*
671  * proc interface
672  */
673 static char *
674 capmode_str(int val)
675 {
676         val &= PERM_READ|PERM_WRITE;
677         if (val == (PERM_READ|PERM_WRITE))
678                 return "read/write";
679         else if (val == PERM_READ)
680                 return "read";
681         else if (val == PERM_WRITE)
682                 return "write";
683         else
684                 return "none";
685 }
686
687 void
688 snd_seq_oss_midi_info_read(snd_info_buffer_t *buf)
689 {
690         int i;
691         seq_oss_midi_t *mdev;
692
693         snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs);
694         for (i = 0; i < max_midi_devs; i++) {
695                 snd_iprintf(buf, "\nmidi %d: ", i);
696                 mdev = get_mdev(i);
697                 if (mdev == NULL) {
698                         snd_iprintf(buf, "*empty*\n");
699                         continue;
700                 }
701                 snd_iprintf(buf, "[%s] ALSA port %d:%d\n", mdev->name,
702                             mdev->client, mdev->port);
703                 snd_iprintf(buf, "  capability %s / opened %s\n",
704                             capmode_str(mdev->flags),
705                             capmode_str(mdev->opened));
706                 snd_use_lock_free(&mdev->use_lock);
707         }
708 }
709