2019-05-27 06:55:05 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Generic MIDI synth driver for ALSA sequencer
|
|
|
|
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
|
2007-10-15 07:50:19 +00:00
|
|
|
* Jaroslav Kysela <perex@perex.cz>
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
Possible options for midisynth module:
|
|
|
|
- automatic opening of midi ports on first received event or subscription
|
|
|
|
(close will be performed when client leaves)
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/string.h>
|
2011-07-15 17:13:37 +00:00
|
|
|
#include <linux/module.h>
|
2006-01-16 15:29:08 +00:00
|
|
|
#include <linux/mutex.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <sound/core.h>
|
|
|
|
#include <sound/rawmidi.h>
|
|
|
|
#include <sound/seq_kernel.h>
|
|
|
|
#include <sound/seq_device.h>
|
|
|
|
#include <sound/seq_midi_event.h>
|
|
|
|
#include <sound/initval.h>
|
|
|
|
|
2007-10-15 07:50:19 +00:00
|
|
|
MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@perex.cz>");
|
2005-04-16 22:20:36 +00:00
|
|
|
MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth.");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
static int output_buffer_size = PAGE_SIZE;
|
|
|
|
module_param(output_buffer_size, int, 0644);
|
|
|
|
MODULE_PARM_DESC(output_buffer_size, "Output buffer size in bytes.");
|
|
|
|
static int input_buffer_size = PAGE_SIZE;
|
|
|
|
module_param(input_buffer_size, int, 0644);
|
|
|
|
MODULE_PARM_DESC(input_buffer_size, "Input buffer size in bytes.");
|
|
|
|
|
|
|
|
/* data for this midi synth driver */
|
2005-11-17 13:04:02 +00:00
|
|
|
struct seq_midisynth {
|
|
|
|
struct snd_card *card;
|
2023-05-23 07:53:22 +00:00
|
|
|
struct snd_rawmidi *rmidi;
|
2005-04-16 22:20:36 +00:00
|
|
|
int device;
|
|
|
|
int subdevice;
|
2005-11-17 13:04:02 +00:00
|
|
|
struct snd_rawmidi_file input_rfile;
|
|
|
|
struct snd_rawmidi_file output_rfile;
|
2005-04-16 22:20:36 +00:00
|
|
|
int seq_client;
|
|
|
|
int seq_port;
|
2005-11-17 13:04:02 +00:00
|
|
|
struct snd_midi_event *parser;
|
|
|
|
};
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-17 13:04:02 +00:00
|
|
|
struct seq_midisynth_client {
|
2005-04-16 22:20:36 +00:00
|
|
|
int seq_client;
|
|
|
|
int num_ports;
|
|
|
|
int ports_per_device[SNDRV_RAWMIDI_DEVICES];
|
2005-11-17 13:04:02 +00:00
|
|
|
struct seq_midisynth *ports[SNDRV_RAWMIDI_DEVICES];
|
|
|
|
};
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-17 13:04:02 +00:00
|
|
|
static struct seq_midisynth_client *synths[SNDRV_CARDS];
|
2006-01-16 15:29:08 +00:00
|
|
|
static DEFINE_MUTEX(register_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* handle rawmidi input event (MIDI v1.0 stream) */
|
2005-11-17 13:04:02 +00:00
|
|
|
static void snd_midi_input_event(struct snd_rawmidi_substream *substream)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 13:04:02 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime;
|
|
|
|
struct seq_midisynth *msynth;
|
|
|
|
struct snd_seq_event ev;
|
2005-04-16 22:20:36 +00:00
|
|
|
char buf[16], *pbuf;
|
2018-08-01 12:38:18 +00:00
|
|
|
long res;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (substream == NULL)
|
|
|
|
return;
|
|
|
|
runtime = substream->runtime;
|
2005-11-17 13:04:02 +00:00
|
|
|
msynth = runtime->private_data;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (msynth == NULL)
|
|
|
|
return;
|
|
|
|
memset(&ev, 0, sizeof(ev));
|
|
|
|
while (runtime->avail > 0) {
|
|
|
|
res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf));
|
|
|
|
if (res <= 0)
|
|
|
|
continue;
|
|
|
|
if (msynth->parser == NULL)
|
|
|
|
continue;
|
|
|
|
pbuf = buf;
|
2018-08-01 12:38:18 +00:00
|
|
|
while (res-- > 0) {
|
|
|
|
if (!snd_midi_event_encode_byte(msynth->parser,
|
|
|
|
*pbuf++, &ev))
|
|
|
|
continue;
|
|
|
|
ev.source.port = msynth->seq_port;
|
|
|
|
ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
|
|
|
|
snd_seq_kernel_client_dispatch(msynth->seq_client, &ev, 1, 0);
|
|
|
|
/* clear event and reset header */
|
|
|
|
memset(&ev, 0, sizeof(ev));
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-11-17 13:04:02 +00:00
|
|
|
static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, int count)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 13:04:02 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime;
|
2005-04-16 22:20:36 +00:00
|
|
|
int tmp;
|
|
|
|
|
2008-08-08 15:09:09 +00:00
|
|
|
if (snd_BUG_ON(!substream || !buf))
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
runtime = substream->runtime;
|
2021-06-08 14:05:30 +00:00
|
|
|
tmp = runtime->avail;
|
|
|
|
if (tmp < count) {
|
2009-07-13 11:30:22 +00:00
|
|
|
if (printk_ratelimit())
|
2014-02-04 17:24:34 +00:00
|
|
|
pr_err("ALSA: seq_midi: MIDI output buffer overrun\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
if (snd_rawmidi_kernel_write(substream, buf, count) < count)
|
|
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
ALSA: seq: fix function cast warnings
clang-16 points out a control flow integrity (kcfi) issue when event
callbacks get converted to incompatible types:
sound/core/seq/seq_midi.c:135:30: error: cast from 'int (*)(struct snd_rawmidi_substream *, const char *, int)' to 'snd_seq_dump_func_t' (aka 'int (*)(void *, void *, int)') converts to incompatible function type [-Werror,-Wcast-function-type-strict]
135 | snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sound/core/seq/seq_virmidi.c:83:31: error: cast from 'int (*)(struct snd_rawmidi_substream *, const unsigned char *, int)' to 'snd_seq_dump_func_t' (aka 'int (*)(void *, void *, int)') converts to incompatible function type [-Werror,-Wcast-function-type-strict]
83 | snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For addressing those errors, introduce wrapper functions that are used
for callbacks and bridge to the actual function call with pointer
cast.
The code was originally added with the initial ALSA merge in linux-2.5.4.
[ the patch description shamelessly copied from Arnd's original patch
-- tiwai ]
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: Arnd Bergmann <arnd@arndb.de>
Link: https://lore.kernel.org/r/20240213101020.459183-1-arnd@kernel.org
Link: https://lore.kernel.org/r/20240213135343.16411-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-02-13 13:53:43 +00:00
|
|
|
/* callback for snd_seq_dump_var_event(), bridging to dump_midi() */
|
|
|
|
static int __dump_midi(void *ptr, void *buf, int count)
|
|
|
|
{
|
|
|
|
return dump_midi(ptr, buf, count);
|
|
|
|
}
|
|
|
|
|
2005-11-17 13:04:02 +00:00
|
|
|
static int event_process_midi(struct snd_seq_event *ev, int direct,
|
2005-04-16 22:20:36 +00:00
|
|
|
void *private_data, int atomic, int hop)
|
|
|
|
{
|
2005-11-17 13:04:02 +00:00
|
|
|
struct seq_midisynth *msynth = private_data;
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned char msg[10]; /* buffer for constructing midi messages */
|
2005-11-17 13:04:02 +00:00
|
|
|
struct snd_rawmidi_substream *substream;
|
2005-07-21 06:01:22 +00:00
|
|
|
int len;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-08-08 15:09:09 +00:00
|
|
|
if (snd_BUG_ON(!msynth))
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
substream = msynth->output_rfile.output;
|
|
|
|
if (substream == NULL)
|
|
|
|
return -ENODEV;
|
|
|
|
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { /* special case, to save space */
|
|
|
|
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
|
|
|
|
/* invalid event */
|
2014-02-04 17:24:34 +00:00
|
|
|
pr_debug("ALSA: seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
ALSA: seq: fix function cast warnings
clang-16 points out a control flow integrity (kcfi) issue when event
callbacks get converted to incompatible types:
sound/core/seq/seq_midi.c:135:30: error: cast from 'int (*)(struct snd_rawmidi_substream *, const char *, int)' to 'snd_seq_dump_func_t' (aka 'int (*)(void *, void *, int)') converts to incompatible function type [-Werror,-Wcast-function-type-strict]
135 | snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sound/core/seq/seq_virmidi.c:83:31: error: cast from 'int (*)(struct snd_rawmidi_substream *, const unsigned char *, int)' to 'snd_seq_dump_func_t' (aka 'int (*)(void *, void *, int)') converts to incompatible function type [-Werror,-Wcast-function-type-strict]
83 | snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For addressing those errors, introduce wrapper functions that are used
for callbacks and bridge to the actual function call with pointer
cast.
The code was originally added with the initial ALSA merge in linux-2.5.4.
[ the patch description shamelessly copied from Arnd's original patch
-- tiwai ]
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: Arnd Bergmann <arnd@arndb.de>
Link: https://lore.kernel.org/r/20240213101020.459183-1-arnd@kernel.org
Link: https://lore.kernel.org/r/20240213135343.16411-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-02-13 13:53:43 +00:00
|
|
|
snd_seq_dump_var_event(ev, __dump_midi, substream);
|
2005-04-16 22:20:36 +00:00
|
|
|
snd_midi_event_reset_decode(msynth->parser);
|
|
|
|
} else {
|
|
|
|
if (msynth->parser == NULL)
|
|
|
|
return -EIO;
|
2005-07-21 06:01:22 +00:00
|
|
|
len = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev);
|
|
|
|
if (len < 0)
|
|
|
|
return 0;
|
|
|
|
if (dump_midi(substream, msg, len) < 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
snd_midi_event_reset_decode(msynth->parser);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-11-17 13:04:02 +00:00
|
|
|
static int snd_seq_midisynth_new(struct seq_midisynth *msynth,
|
|
|
|
struct snd_card *card,
|
2005-04-16 22:20:36 +00:00
|
|
|
int device,
|
|
|
|
int subdevice)
|
|
|
|
{
|
|
|
|
if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &msynth->parser) < 0)
|
|
|
|
return -ENOMEM;
|
|
|
|
msynth->card = card;
|
|
|
|
msynth->device = device;
|
|
|
|
msynth->subdevice = subdevice;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* open associated midi device for input */
|
2005-11-17 13:04:02 +00:00
|
|
|
static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe *info)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int err;
|
2005-11-17 13:04:02 +00:00
|
|
|
struct seq_midisynth *msynth = private_data;
|
|
|
|
struct snd_rawmidi_runtime *runtime;
|
|
|
|
struct snd_rawmidi_params params;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* open midi port */
|
2023-05-23 07:53:22 +00:00
|
|
|
err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice,
|
2021-06-08 14:05:30 +00:00
|
|
|
SNDRV_RAWMIDI_LFLG_INPUT,
|
|
|
|
&msynth->input_rfile);
|
|
|
|
if (err < 0) {
|
2014-02-04 17:24:34 +00:00
|
|
|
pr_debug("ALSA: seq_midi: midi input open failed!!!\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
runtime = msynth->input_rfile.input->runtime;
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
params.avail_min = 1;
|
|
|
|
params.buffer_size = input_buffer_size;
|
2021-06-08 14:05:30 +00:00
|
|
|
err = snd_rawmidi_input_params(msynth->input_rfile.input, ¶ms);
|
|
|
|
if (err < 0) {
|
2005-04-16 22:20:36 +00:00
|
|
|
snd_rawmidi_kernel_release(&msynth->input_rfile);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
snd_midi_event_reset_encode(msynth->parser);
|
|
|
|
runtime->event = snd_midi_input_event;
|
|
|
|
runtime->private_data = msynth;
|
|
|
|
snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* close associated midi device for input */
|
2005-11-17 13:04:02 +00:00
|
|
|
static int midisynth_unsubscribe(void *private_data, struct snd_seq_port_subscribe *info)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int err;
|
2005-11-17 13:04:02 +00:00
|
|
|
struct seq_midisynth *msynth = private_data;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-08-08 15:09:09 +00:00
|
|
|
if (snd_BUG_ON(!msynth->input_rfile.input))
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
err = snd_rawmidi_kernel_release(&msynth->input_rfile);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* open associated midi device for output */
|
2005-11-17 13:04:02 +00:00
|
|
|
static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int err;
|
2005-11-17 13:04:02 +00:00
|
|
|
struct seq_midisynth *msynth = private_data;
|
|
|
|
struct snd_rawmidi_params params;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* open midi port */
|
2023-05-23 07:53:22 +00:00
|
|
|
err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice,
|
2021-06-08 14:05:30 +00:00
|
|
|
SNDRV_RAWMIDI_LFLG_OUTPUT,
|
|
|
|
&msynth->output_rfile);
|
|
|
|
if (err < 0) {
|
2014-02-04 17:24:34 +00:00
|
|
|
pr_debug("ALSA: seq_midi: midi output open failed!!!\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
params.avail_min = 1;
|
|
|
|
params.buffer_size = output_buffer_size;
|
2009-07-13 11:52:46 +00:00
|
|
|
params.no_active_sensing = 1;
|
2021-06-08 14:05:30 +00:00
|
|
|
err = snd_rawmidi_output_params(msynth->output_rfile.output, ¶ms);
|
|
|
|
if (err < 0) {
|
2005-04-16 22:20:36 +00:00
|
|
|
snd_rawmidi_kernel_release(&msynth->output_rfile);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
snd_midi_event_reset_decode(msynth->parser);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* close associated midi device for output */
|
2005-11-17 13:04:02 +00:00
|
|
|
static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 13:04:02 +00:00
|
|
|
struct seq_midisynth *msynth = private_data;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-08-08 15:09:09 +00:00
|
|
|
if (snd_BUG_ON(!msynth->output_rfile.output))
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
snd_rawmidi_drain_output(msynth->output_rfile.output);
|
|
|
|
return snd_rawmidi_kernel_release(&msynth->output_rfile);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* delete given midi synth port */
|
2005-11-17 13:04:02 +00:00
|
|
|
static void snd_seq_midisynth_delete(struct seq_midisynth *msynth)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
if (msynth == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (msynth->seq_client > 0) {
|
|
|
|
/* delete port */
|
|
|
|
snd_seq_event_port_detach(msynth->seq_client, msynth->seq_port);
|
|
|
|
}
|
|
|
|
|
2015-01-03 13:54:56 +00:00
|
|
|
snd_midi_event_free(msynth->parser);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* register new midi synth port */
|
|
|
|
static int
|
2015-02-12 12:43:22 +00:00
|
|
|
snd_seq_midisynth_probe(struct device *_dev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2015-02-12 12:43:22 +00:00
|
|
|
struct snd_seq_device *dev = to_seq_dev(_dev);
|
2005-11-17 13:04:02 +00:00
|
|
|
struct seq_midisynth_client *client;
|
|
|
|
struct seq_midisynth *msynth, *ms;
|
2024-02-22 11:15:09 +00:00
|
|
|
struct snd_seq_port_info *port __free(kfree) = NULL;
|
|
|
|
struct snd_rawmidi_info *info __free(kfree) = NULL;
|
2006-05-02 14:22:12 +00:00
|
|
|
struct snd_rawmidi *rmidi = dev->private_data;
|
2005-04-16 22:20:36 +00:00
|
|
|
int newclient = 0;
|
|
|
|
unsigned int p, ports;
|
2005-11-17 13:04:02 +00:00
|
|
|
struct snd_seq_port_callback pcallbacks;
|
|
|
|
struct snd_card *card = dev->card;
|
2005-04-16 22:20:36 +00:00
|
|
|
int device = dev->device;
|
|
|
|
unsigned int input_count = 0, output_count = 0;
|
|
|
|
|
2008-08-08 15:09:09 +00:00
|
|
|
if (snd_BUG_ON(!card || device < 0 || device >= SNDRV_RAWMIDI_DEVICES))
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
|
|
|
if (! info)
|
|
|
|
return -ENOMEM;
|
|
|
|
info->device = device;
|
|
|
|
info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
|
|
|
|
info->subdevice = 0;
|
|
|
|
if (snd_rawmidi_info_select(card, info) >= 0)
|
|
|
|
output_count = info->subdevices_count;
|
|
|
|
info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
|
|
|
|
if (snd_rawmidi_info_select(card, info) >= 0) {
|
|
|
|
input_count = info->subdevices_count;
|
|
|
|
}
|
|
|
|
ports = output_count;
|
|
|
|
if (ports < input_count)
|
|
|
|
ports = input_count;
|
2024-02-22 11:15:09 +00:00
|
|
|
if (ports == 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
|
|
|
if (ports > (256 / SNDRV_RAWMIDI_DEVICES))
|
|
|
|
ports = 256 / SNDRV_RAWMIDI_DEVICES;
|
|
|
|
|
2024-02-27 08:52:59 +00:00
|
|
|
guard(mutex)(®ister_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
client = synths[card->number];
|
|
|
|
if (client == NULL) {
|
|
|
|
newclient = 1;
|
2005-09-09 12:20:49 +00:00
|
|
|
client = kzalloc(sizeof(*client), GFP_KERNEL);
|
2024-02-27 08:52:59 +00:00
|
|
|
if (client == NULL)
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENOMEM;
|
2005-12-12 08:33:37 +00:00
|
|
|
client->seq_client =
|
|
|
|
snd_seq_create_kernel_client(
|
2006-04-21 06:39:20 +00:00
|
|
|
card, 0, "%s", card->shortname[0] ?
|
|
|
|
(const char *)card->shortname : "External MIDI");
|
2005-04-16 22:20:36 +00:00
|
|
|
if (client->seq_client < 0) {
|
|
|
|
kfree(client);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2005-12-12 08:33:37 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-17 13:04:02 +00:00
|
|
|
msynth = kcalloc(ports, sizeof(struct seq_midisynth), GFP_KERNEL);
|
2005-04-16 22:20:36 +00:00
|
|
|
port = kmalloc(sizeof(*port), GFP_KERNEL);
|
|
|
|
if (msynth == NULL || port == NULL)
|
|
|
|
goto __nomem;
|
|
|
|
|
|
|
|
for (p = 0; p < ports; p++) {
|
|
|
|
ms = &msynth[p];
|
2023-05-23 07:53:22 +00:00
|
|
|
ms->rmidi = rmidi;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (snd_seq_midisynth_new(ms, card, device, p) < 0)
|
|
|
|
goto __nomem;
|
|
|
|
|
|
|
|
/* declare port */
|
|
|
|
memset(port, 0, sizeof(*port));
|
|
|
|
port->addr.client = client->seq_client;
|
|
|
|
port->addr.port = device * (256 / SNDRV_RAWMIDI_DEVICES) + p;
|
|
|
|
port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
|
|
|
|
memset(info, 0, sizeof(*info));
|
|
|
|
info->device = device;
|
|
|
|
if (p < output_count)
|
|
|
|
info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
|
|
|
|
else
|
|
|
|
info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
|
|
|
|
info->subdevice = p;
|
|
|
|
if (snd_rawmidi_info_select(card, info) >= 0)
|
|
|
|
strcpy(port->name, info->subname);
|
|
|
|
if (! port->name[0]) {
|
|
|
|
if (info->name[0]) {
|
|
|
|
if (ports > 1)
|
2023-09-15 08:27:51 +00:00
|
|
|
scnprintf(port->name, sizeof(port->name), "%s-%u", info->name, p);
|
2005-04-16 22:20:36 +00:00
|
|
|
else
|
2023-09-15 08:27:51 +00:00
|
|
|
scnprintf(port->name, sizeof(port->name), "%s", info->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
} else {
|
|
|
|
/* last resort */
|
|
|
|
if (ports > 1)
|
2014-04-22 11:42:05 +00:00
|
|
|
sprintf(port->name, "MIDI %d-%d-%u", card->number, device, p);
|
2005-04-16 22:20:36 +00:00
|
|
|
else
|
|
|
|
sprintf(port->name, "MIDI %d-%d", card->number, device);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((info->flags & SNDRV_RAWMIDI_INFO_OUTPUT) && p < output_count)
|
|
|
|
port->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
|
|
|
|
if ((info->flags & SNDRV_RAWMIDI_INFO_INPUT) && p < input_count)
|
|
|
|
port->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
|
|
|
|
if ((port->capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) &&
|
|
|
|
info->flags & SNDRV_RAWMIDI_INFO_DUPLEX)
|
|
|
|
port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
|
2023-05-23 07:53:49 +00:00
|
|
|
if (port->capability & SNDRV_SEQ_PORT_CAP_READ)
|
|
|
|
port->direction |= SNDRV_SEQ_PORT_DIR_INPUT;
|
|
|
|
if (port->capability & SNDRV_SEQ_PORT_CAP_WRITE)
|
|
|
|
port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT;
|
2006-05-02 14:08:41 +00:00
|
|
|
port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
|
|
|
|
| SNDRV_SEQ_PORT_TYPE_HARDWARE
|
|
|
|
| SNDRV_SEQ_PORT_TYPE_PORT;
|
2005-04-16 22:20:36 +00:00
|
|
|
port->midi_channels = 16;
|
|
|
|
memset(&pcallbacks, 0, sizeof(pcallbacks));
|
|
|
|
pcallbacks.owner = THIS_MODULE;
|
|
|
|
pcallbacks.private_data = ms;
|
|
|
|
pcallbacks.subscribe = midisynth_subscribe;
|
|
|
|
pcallbacks.unsubscribe = midisynth_unsubscribe;
|
|
|
|
pcallbacks.use = midisynth_use;
|
|
|
|
pcallbacks.unuse = midisynth_unuse;
|
|
|
|
pcallbacks.event_input = event_process_midi;
|
|
|
|
port->kernel = &pcallbacks;
|
2006-05-02 14:22:12 +00:00
|
|
|
if (rmidi->ops && rmidi->ops->get_port_info)
|
|
|
|
rmidi->ops->get_port_info(rmidi, p, port);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_CREATE_PORT, port)<0)
|
|
|
|
goto __nomem;
|
|
|
|
ms->seq_client = client->seq_client;
|
|
|
|
ms->seq_port = port->addr.port;
|
|
|
|
}
|
|
|
|
client->ports_per_device[device] = ports;
|
|
|
|
client->ports[device] = msynth;
|
|
|
|
client->num_ports++;
|
|
|
|
if (newclient)
|
|
|
|
synths[card->number] = client;
|
|
|
|
return 0; /* success */
|
|
|
|
|
|
|
|
__nomem:
|
|
|
|
if (msynth != NULL) {
|
|
|
|
for (p = 0; p < ports; p++)
|
|
|
|
snd_seq_midisynth_delete(&msynth[p]);
|
|
|
|
kfree(msynth);
|
|
|
|
}
|
|
|
|
if (newclient) {
|
|
|
|
snd_seq_delete_kernel_client(client->seq_client);
|
|
|
|
kfree(client);
|
|
|
|
}
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* release midi synth port */
|
|
|
|
static int
|
2015-02-12 12:43:22 +00:00
|
|
|
snd_seq_midisynth_remove(struct device *_dev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2015-02-12 12:43:22 +00:00
|
|
|
struct snd_seq_device *dev = to_seq_dev(_dev);
|
2005-11-17 13:04:02 +00:00
|
|
|
struct seq_midisynth_client *client;
|
|
|
|
struct seq_midisynth *msynth;
|
|
|
|
struct snd_card *card = dev->card;
|
2005-04-16 22:20:36 +00:00
|
|
|
int device = dev->device, p, ports;
|
|
|
|
|
2024-02-27 08:52:59 +00:00
|
|
|
guard(mutex)(®ister_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
client = synths[card->number];
|
2024-02-27 08:52:59 +00:00
|
|
|
if (client == NULL || client->ports[device] == NULL)
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
|
|
|
ports = client->ports_per_device[device];
|
|
|
|
client->ports_per_device[device] = 0;
|
|
|
|
msynth = client->ports[device];
|
|
|
|
client->ports[device] = NULL;
|
|
|
|
for (p = 0; p < ports; p++)
|
|
|
|
snd_seq_midisynth_delete(&msynth[p]);
|
|
|
|
kfree(msynth);
|
|
|
|
client->num_ports--;
|
|
|
|
if (client->num_ports <= 0) {
|
|
|
|
snd_seq_delete_kernel_client(client->seq_client);
|
|
|
|
synths[card->number] = NULL;
|
|
|
|
kfree(client);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-02-12 12:43:22 +00:00
|
|
|
static struct snd_seq_driver seq_midisynth_driver = {
|
|
|
|
.driver = {
|
|
|
|
.name = KBUILD_MODNAME,
|
|
|
|
.probe = snd_seq_midisynth_probe,
|
|
|
|
.remove = snd_seq_midisynth_remove,
|
|
|
|
},
|
|
|
|
.id = SNDRV_SEQ_DEV_ID_MIDISYNTH,
|
|
|
|
.argsize = 0,
|
|
|
|
};
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2015-02-12 13:20:24 +00:00
|
|
|
module_snd_seq_driver(seq_midisynth_driver);
|