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