cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

seq_oss_midi.c (16151B)


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