11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * Generic MIDI synth driver for ALSA sequencer 41da177e4SLinus Torvalds * Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl> 5c1017a4cSJaroslav Kysela * Jaroslav Kysela <perex@perex.cz> 61da177e4SLinus Torvalds */ 71da177e4SLinus Torvalds 81da177e4SLinus Torvalds /* 91da177e4SLinus Torvalds Possible options for midisynth module: 101da177e4SLinus Torvalds - automatic opening of midi ports on first received event or subscription 111da177e4SLinus Torvalds (close will be performed when client leaves) 121da177e4SLinus Torvalds */ 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds 151da177e4SLinus Torvalds #include <linux/init.h> 161da177e4SLinus Torvalds #include <linux/slab.h> 171da177e4SLinus Torvalds #include <linux/errno.h> 181da177e4SLinus Torvalds #include <linux/string.h> 1965a77217SPaul Gortmaker #include <linux/module.h> 201a60d4c5SIngo Molnar #include <linux/mutex.h> 211da177e4SLinus Torvalds #include <sound/core.h> 221da177e4SLinus Torvalds #include <sound/rawmidi.h> 231da177e4SLinus Torvalds #include <sound/seq_kernel.h> 241da177e4SLinus Torvalds #include <sound/seq_device.h> 251da177e4SLinus Torvalds #include <sound/seq_midi_event.h> 261da177e4SLinus Torvalds #include <sound/initval.h> 271da177e4SLinus Torvalds 28c1017a4cSJaroslav Kysela MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@perex.cz>"); 291da177e4SLinus Torvalds MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth."); 301da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 311da177e4SLinus Torvalds static int output_buffer_size = PAGE_SIZE; 321da177e4SLinus Torvalds module_param(output_buffer_size, int, 0644); 331da177e4SLinus Torvalds MODULE_PARM_DESC(output_buffer_size, "Output buffer size in bytes."); 341da177e4SLinus Torvalds static int input_buffer_size = PAGE_SIZE; 351da177e4SLinus Torvalds module_param(input_buffer_size, int, 0644); 361da177e4SLinus Torvalds MODULE_PARM_DESC(input_buffer_size, "Input buffer size in bytes."); 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds /* data for this midi synth driver */ 39c7e0b5bfSTakashi Iwai struct seq_midisynth { 40c7e0b5bfSTakashi Iwai struct snd_card *card; 41*09b62892STakashi Iwai struct snd_rawmidi *rmidi; 421da177e4SLinus Torvalds int device; 431da177e4SLinus Torvalds int subdevice; 44c7e0b5bfSTakashi Iwai struct snd_rawmidi_file input_rfile; 45c7e0b5bfSTakashi Iwai struct snd_rawmidi_file output_rfile; 461da177e4SLinus Torvalds int seq_client; 471da177e4SLinus Torvalds int seq_port; 48c7e0b5bfSTakashi Iwai struct snd_midi_event *parser; 49c7e0b5bfSTakashi Iwai }; 501da177e4SLinus Torvalds 51c7e0b5bfSTakashi Iwai struct seq_midisynth_client { 521da177e4SLinus Torvalds int seq_client; 531da177e4SLinus Torvalds int num_ports; 541da177e4SLinus Torvalds int ports_per_device[SNDRV_RAWMIDI_DEVICES]; 55c7e0b5bfSTakashi Iwai struct seq_midisynth *ports[SNDRV_RAWMIDI_DEVICES]; 56c7e0b5bfSTakashi Iwai }; 571da177e4SLinus Torvalds 58c7e0b5bfSTakashi Iwai static struct seq_midisynth_client *synths[SNDRV_CARDS]; 591a60d4c5SIngo Molnar static DEFINE_MUTEX(register_mutex); 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds /* handle rawmidi input event (MIDI v1.0 stream) */ 62c7e0b5bfSTakashi Iwai static void snd_midi_input_event(struct snd_rawmidi_substream *substream) 631da177e4SLinus Torvalds { 64c7e0b5bfSTakashi Iwai struct snd_rawmidi_runtime *runtime; 65c7e0b5bfSTakashi Iwai struct seq_midisynth *msynth; 66c7e0b5bfSTakashi Iwai struct snd_seq_event ev; 671da177e4SLinus Torvalds char buf[16], *pbuf; 68ef965ad5STakashi Iwai long res; 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds if (substream == NULL) 711da177e4SLinus Torvalds return; 721da177e4SLinus Torvalds runtime = substream->runtime; 73c7e0b5bfSTakashi Iwai msynth = runtime->private_data; 741da177e4SLinus Torvalds if (msynth == NULL) 751da177e4SLinus Torvalds return; 761da177e4SLinus Torvalds memset(&ev, 0, sizeof(ev)); 771da177e4SLinus Torvalds while (runtime->avail > 0) { 781da177e4SLinus Torvalds res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf)); 791da177e4SLinus Torvalds if (res <= 0) 801da177e4SLinus Torvalds continue; 811da177e4SLinus Torvalds if (msynth->parser == NULL) 821da177e4SLinus Torvalds continue; 831da177e4SLinus Torvalds pbuf = buf; 84ef965ad5STakashi Iwai while (res-- > 0) { 85ef965ad5STakashi Iwai if (!snd_midi_event_encode_byte(msynth->parser, 86ef965ad5STakashi Iwai *pbuf++, &ev)) 87ef965ad5STakashi Iwai continue; 881da177e4SLinus Torvalds ev.source.port = msynth->seq_port; 891da177e4SLinus Torvalds ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; 901da177e4SLinus Torvalds snd_seq_kernel_client_dispatch(msynth->seq_client, &ev, 1, 0); 911da177e4SLinus Torvalds /* clear event and reset header */ 921da177e4SLinus Torvalds memset(&ev, 0, sizeof(ev)); 931da177e4SLinus Torvalds } 941da177e4SLinus Torvalds } 951da177e4SLinus Torvalds } 961da177e4SLinus Torvalds 97c7e0b5bfSTakashi Iwai static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, int count) 981da177e4SLinus Torvalds { 99c7e0b5bfSTakashi Iwai struct snd_rawmidi_runtime *runtime; 1001da177e4SLinus Torvalds int tmp; 1011da177e4SLinus Torvalds 1027eaa943cSTakashi Iwai if (snd_BUG_ON(!substream || !buf)) 1037eaa943cSTakashi Iwai return -EINVAL; 1041da177e4SLinus Torvalds runtime = substream->runtime; 105f9a6bb84STakashi Iwai tmp = runtime->avail; 106f9a6bb84STakashi Iwai if (tmp < count) { 107f907ed94SClemens Ladisch if (printk_ratelimit()) 10804cc79a0STakashi Iwai pr_err("ALSA: seq_midi: MIDI output buffer overrun\n"); 1091da177e4SLinus Torvalds return -ENOMEM; 1101da177e4SLinus Torvalds } 1111da177e4SLinus Torvalds if (snd_rawmidi_kernel_write(substream, buf, count) < count) 1121da177e4SLinus Torvalds return -EINVAL; 1131da177e4SLinus Torvalds return 0; 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds 116c7e0b5bfSTakashi Iwai static int event_process_midi(struct snd_seq_event *ev, int direct, 1171da177e4SLinus Torvalds void *private_data, int atomic, int hop) 1181da177e4SLinus Torvalds { 119c7e0b5bfSTakashi Iwai struct seq_midisynth *msynth = private_data; 1201da177e4SLinus Torvalds unsigned char msg[10]; /* buffer for constructing midi messages */ 121c7e0b5bfSTakashi Iwai struct snd_rawmidi_substream *substream; 122d06e4c40SClemens Ladisch int len; 1231da177e4SLinus Torvalds 1247eaa943cSTakashi Iwai if (snd_BUG_ON(!msynth)) 1257eaa943cSTakashi Iwai return -EINVAL; 1261da177e4SLinus Torvalds substream = msynth->output_rfile.output; 1271da177e4SLinus Torvalds if (substream == NULL) 1281da177e4SLinus Torvalds return -ENODEV; 1291da177e4SLinus Torvalds if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { /* special case, to save space */ 1301da177e4SLinus Torvalds if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) { 1311da177e4SLinus Torvalds /* invalid event */ 13204cc79a0STakashi Iwai pr_debug("ALSA: seq_midi: invalid sysex event flags = 0x%x\n", ev->flags); 1331da177e4SLinus Torvalds return 0; 1341da177e4SLinus Torvalds } 135d06e4c40SClemens Ladisch snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream); 1361da177e4SLinus Torvalds snd_midi_event_reset_decode(msynth->parser); 1371da177e4SLinus Torvalds } else { 1381da177e4SLinus Torvalds if (msynth->parser == NULL) 1391da177e4SLinus Torvalds return -EIO; 140d06e4c40SClemens Ladisch len = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev); 141d06e4c40SClemens Ladisch if (len < 0) 142d06e4c40SClemens Ladisch return 0; 143d06e4c40SClemens Ladisch if (dump_midi(substream, msg, len) < 0) 1441da177e4SLinus Torvalds snd_midi_event_reset_decode(msynth->parser); 1451da177e4SLinus Torvalds } 1461da177e4SLinus Torvalds return 0; 1471da177e4SLinus Torvalds } 1481da177e4SLinus Torvalds 1491da177e4SLinus Torvalds 150c7e0b5bfSTakashi Iwai static int snd_seq_midisynth_new(struct seq_midisynth *msynth, 151c7e0b5bfSTakashi Iwai struct snd_card *card, 1521da177e4SLinus Torvalds int device, 1531da177e4SLinus Torvalds int subdevice) 1541da177e4SLinus Torvalds { 1551da177e4SLinus Torvalds if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &msynth->parser) < 0) 1561da177e4SLinus Torvalds return -ENOMEM; 1571da177e4SLinus Torvalds msynth->card = card; 1581da177e4SLinus Torvalds msynth->device = device; 1591da177e4SLinus Torvalds msynth->subdevice = subdevice; 1601da177e4SLinus Torvalds return 0; 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds 1631da177e4SLinus Torvalds /* open associated midi device for input */ 164c7e0b5bfSTakashi Iwai static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe *info) 1651da177e4SLinus Torvalds { 1661da177e4SLinus Torvalds int err; 167c7e0b5bfSTakashi Iwai struct seq_midisynth *msynth = private_data; 168c7e0b5bfSTakashi Iwai struct snd_rawmidi_runtime *runtime; 169c7e0b5bfSTakashi Iwai struct snd_rawmidi_params params; 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds /* open midi port */ 172*09b62892STakashi Iwai err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice, 173f87135f5SClemens Ladisch SNDRV_RAWMIDI_LFLG_INPUT, 174f9a6bb84STakashi Iwai &msynth->input_rfile); 175f9a6bb84STakashi Iwai if (err < 0) { 17604cc79a0STakashi Iwai pr_debug("ALSA: seq_midi: midi input open failed!!!\n"); 1771da177e4SLinus Torvalds return err; 1781da177e4SLinus Torvalds } 1791da177e4SLinus Torvalds runtime = msynth->input_rfile.input->runtime; 1801da177e4SLinus Torvalds memset(¶ms, 0, sizeof(params)); 1811da177e4SLinus Torvalds params.avail_min = 1; 1821da177e4SLinus Torvalds params.buffer_size = input_buffer_size; 183f9a6bb84STakashi Iwai err = snd_rawmidi_input_params(msynth->input_rfile.input, ¶ms); 184f9a6bb84STakashi Iwai if (err < 0) { 1851da177e4SLinus Torvalds snd_rawmidi_kernel_release(&msynth->input_rfile); 1861da177e4SLinus Torvalds return err; 1871da177e4SLinus Torvalds } 1881da177e4SLinus Torvalds snd_midi_event_reset_encode(msynth->parser); 1891da177e4SLinus Torvalds runtime->event = snd_midi_input_event; 1901da177e4SLinus Torvalds runtime->private_data = msynth; 1911da177e4SLinus Torvalds snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0); 1921da177e4SLinus Torvalds return 0; 1931da177e4SLinus Torvalds } 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds /* close associated midi device for input */ 196c7e0b5bfSTakashi Iwai static int midisynth_unsubscribe(void *private_data, struct snd_seq_port_subscribe *info) 1971da177e4SLinus Torvalds { 1981da177e4SLinus Torvalds int err; 199c7e0b5bfSTakashi Iwai struct seq_midisynth *msynth = private_data; 2001da177e4SLinus Torvalds 2017eaa943cSTakashi Iwai if (snd_BUG_ON(!msynth->input_rfile.input)) 2027eaa943cSTakashi Iwai return -EINVAL; 2031da177e4SLinus Torvalds err = snd_rawmidi_kernel_release(&msynth->input_rfile); 2041da177e4SLinus Torvalds return err; 2051da177e4SLinus Torvalds } 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds /* open associated midi device for output */ 208c7e0b5bfSTakashi Iwai static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info) 2091da177e4SLinus Torvalds { 2101da177e4SLinus Torvalds int err; 211c7e0b5bfSTakashi Iwai struct seq_midisynth *msynth = private_data; 212c7e0b5bfSTakashi Iwai struct snd_rawmidi_params params; 2131da177e4SLinus Torvalds 2141da177e4SLinus Torvalds /* open midi port */ 215*09b62892STakashi Iwai err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice, 216f87135f5SClemens Ladisch SNDRV_RAWMIDI_LFLG_OUTPUT, 217f9a6bb84STakashi Iwai &msynth->output_rfile); 218f9a6bb84STakashi Iwai if (err < 0) { 21904cc79a0STakashi Iwai pr_debug("ALSA: seq_midi: midi output open failed!!!\n"); 2201da177e4SLinus Torvalds return err; 2211da177e4SLinus Torvalds } 2221da177e4SLinus Torvalds memset(¶ms, 0, sizeof(params)); 2231da177e4SLinus Torvalds params.avail_min = 1; 2241da177e4SLinus Torvalds params.buffer_size = output_buffer_size; 2252d4b8420SClemens Ladisch params.no_active_sensing = 1; 226f9a6bb84STakashi Iwai err = snd_rawmidi_output_params(msynth->output_rfile.output, ¶ms); 227f9a6bb84STakashi Iwai if (err < 0) { 2281da177e4SLinus Torvalds snd_rawmidi_kernel_release(&msynth->output_rfile); 2291da177e4SLinus Torvalds return err; 2301da177e4SLinus Torvalds } 2311da177e4SLinus Torvalds snd_midi_event_reset_decode(msynth->parser); 2321da177e4SLinus Torvalds return 0; 2331da177e4SLinus Torvalds } 2341da177e4SLinus Torvalds 2351da177e4SLinus Torvalds /* close associated midi device for output */ 236c7e0b5bfSTakashi Iwai static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info) 2371da177e4SLinus Torvalds { 238c7e0b5bfSTakashi Iwai struct seq_midisynth *msynth = private_data; 2391da177e4SLinus Torvalds 2407eaa943cSTakashi Iwai if (snd_BUG_ON(!msynth->output_rfile.output)) 2417eaa943cSTakashi Iwai return -EINVAL; 2421da177e4SLinus Torvalds snd_rawmidi_drain_output(msynth->output_rfile.output); 2431da177e4SLinus Torvalds return snd_rawmidi_kernel_release(&msynth->output_rfile); 2441da177e4SLinus Torvalds } 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds /* delete given midi synth port */ 247c7e0b5bfSTakashi Iwai static void snd_seq_midisynth_delete(struct seq_midisynth *msynth) 2481da177e4SLinus Torvalds { 2491da177e4SLinus Torvalds if (msynth == NULL) 2501da177e4SLinus Torvalds return; 2511da177e4SLinus Torvalds 2521da177e4SLinus Torvalds if (msynth->seq_client > 0) { 2531da177e4SLinus Torvalds /* delete port */ 2541da177e4SLinus Torvalds snd_seq_event_port_detach(msynth->seq_client, msynth->seq_port); 2551da177e4SLinus Torvalds } 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds snd_midi_event_free(msynth->parser); 2581da177e4SLinus Torvalds } 2591da177e4SLinus Torvalds 2601da177e4SLinus Torvalds /* register new midi synth port */ 2611da177e4SLinus Torvalds static int 26205662205STakashi Iwai snd_seq_midisynth_probe(struct device *_dev) 2631da177e4SLinus Torvalds { 26405662205STakashi Iwai struct snd_seq_device *dev = to_seq_dev(_dev); 265c7e0b5bfSTakashi Iwai struct seq_midisynth_client *client; 266c7e0b5bfSTakashi Iwai struct seq_midisynth *msynth, *ms; 267c7e0b5bfSTakashi Iwai struct snd_seq_port_info *port; 268c7e0b5bfSTakashi Iwai struct snd_rawmidi_info *info; 269a7b928acSClemens Ladisch struct snd_rawmidi *rmidi = dev->private_data; 2701da177e4SLinus Torvalds int newclient = 0; 2711da177e4SLinus Torvalds unsigned int p, ports; 272c7e0b5bfSTakashi Iwai struct snd_seq_port_callback pcallbacks; 273c7e0b5bfSTakashi Iwai struct snd_card *card = dev->card; 2741da177e4SLinus Torvalds int device = dev->device; 2751da177e4SLinus Torvalds unsigned int input_count = 0, output_count = 0; 2761da177e4SLinus Torvalds 2777eaa943cSTakashi Iwai if (snd_BUG_ON(!card || device < 0 || device >= SNDRV_RAWMIDI_DEVICES)) 2787eaa943cSTakashi Iwai return -EINVAL; 2791da177e4SLinus Torvalds info = kmalloc(sizeof(*info), GFP_KERNEL); 2801da177e4SLinus Torvalds if (! info) 2811da177e4SLinus Torvalds return -ENOMEM; 2821da177e4SLinus Torvalds info->device = device; 2831da177e4SLinus Torvalds info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT; 2841da177e4SLinus Torvalds info->subdevice = 0; 2851da177e4SLinus Torvalds if (snd_rawmidi_info_select(card, info) >= 0) 2861da177e4SLinus Torvalds output_count = info->subdevices_count; 2871da177e4SLinus Torvalds info->stream = SNDRV_RAWMIDI_STREAM_INPUT; 2881da177e4SLinus Torvalds if (snd_rawmidi_info_select(card, info) >= 0) { 2891da177e4SLinus Torvalds input_count = info->subdevices_count; 2901da177e4SLinus Torvalds } 2911da177e4SLinus Torvalds ports = output_count; 2921da177e4SLinus Torvalds if (ports < input_count) 2931da177e4SLinus Torvalds ports = input_count; 2941da177e4SLinus Torvalds if (ports == 0) { 2951da177e4SLinus Torvalds kfree(info); 2961da177e4SLinus Torvalds return -ENODEV; 2971da177e4SLinus Torvalds } 2981da177e4SLinus Torvalds if (ports > (256 / SNDRV_RAWMIDI_DEVICES)) 2991da177e4SLinus Torvalds ports = 256 / SNDRV_RAWMIDI_DEVICES; 3001da177e4SLinus Torvalds 3011a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 3021da177e4SLinus Torvalds client = synths[card->number]; 3031da177e4SLinus Torvalds if (client == NULL) { 3041da177e4SLinus Torvalds newclient = 1; 305ecca82b4STakashi Iwai client = kzalloc(sizeof(*client), GFP_KERNEL); 3061da177e4SLinus Torvalds if (client == NULL) { 3071a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 3081da177e4SLinus Torvalds kfree(info); 3091da177e4SLinus Torvalds return -ENOMEM; 3101da177e4SLinus Torvalds } 3117b6d9245SClemens Ladisch client->seq_client = 3127b6d9245SClemens Ladisch snd_seq_create_kernel_client( 31378fc030bSAlan Horstmann card, 0, "%s", card->shortname[0] ? 31478fc030bSAlan Horstmann (const char *)card->shortname : "External MIDI"); 3151da177e4SLinus Torvalds if (client->seq_client < 0) { 3161da177e4SLinus Torvalds kfree(client); 3171a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 3181da177e4SLinus Torvalds kfree(info); 3191da177e4SLinus Torvalds return -ENOMEM; 3201da177e4SLinus Torvalds } 3217b6d9245SClemens Ladisch } 3221da177e4SLinus Torvalds 323c7e0b5bfSTakashi Iwai msynth = kcalloc(ports, sizeof(struct seq_midisynth), GFP_KERNEL); 3241da177e4SLinus Torvalds port = kmalloc(sizeof(*port), GFP_KERNEL); 3251da177e4SLinus Torvalds if (msynth == NULL || port == NULL) 3261da177e4SLinus Torvalds goto __nomem; 3271da177e4SLinus Torvalds 3281da177e4SLinus Torvalds for (p = 0; p < ports; p++) { 3291da177e4SLinus Torvalds ms = &msynth[p]; 330*09b62892STakashi Iwai ms->rmidi = rmidi; 3311da177e4SLinus Torvalds 3321da177e4SLinus Torvalds if (snd_seq_midisynth_new(ms, card, device, p) < 0) 3331da177e4SLinus Torvalds goto __nomem; 3341da177e4SLinus Torvalds 3351da177e4SLinus Torvalds /* declare port */ 3361da177e4SLinus Torvalds memset(port, 0, sizeof(*port)); 3371da177e4SLinus Torvalds port->addr.client = client->seq_client; 3381da177e4SLinus Torvalds port->addr.port = device * (256 / SNDRV_RAWMIDI_DEVICES) + p; 3391da177e4SLinus Torvalds port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; 3401da177e4SLinus Torvalds memset(info, 0, sizeof(*info)); 3411da177e4SLinus Torvalds info->device = device; 3421da177e4SLinus Torvalds if (p < output_count) 3431da177e4SLinus Torvalds info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT; 3441da177e4SLinus Torvalds else 3451da177e4SLinus Torvalds info->stream = SNDRV_RAWMIDI_STREAM_INPUT; 3461da177e4SLinus Torvalds info->subdevice = p; 3471da177e4SLinus Torvalds if (snd_rawmidi_info_select(card, info) >= 0) 3481da177e4SLinus Torvalds strcpy(port->name, info->subname); 3491da177e4SLinus Torvalds if (! port->name[0]) { 3501da177e4SLinus Torvalds if (info->name[0]) { 3511da177e4SLinus Torvalds if (ports > 1) 35253403a80SMasanari Iida snprintf(port->name, sizeof(port->name), "%s-%u", info->name, p); 3531da177e4SLinus Torvalds else 3541da177e4SLinus Torvalds snprintf(port->name, sizeof(port->name), "%s", info->name); 3551da177e4SLinus Torvalds } else { 3561da177e4SLinus Torvalds /* last resort */ 3571da177e4SLinus Torvalds if (ports > 1) 35853403a80SMasanari Iida sprintf(port->name, "MIDI %d-%d-%u", card->number, device, p); 3591da177e4SLinus Torvalds else 3601da177e4SLinus Torvalds sprintf(port->name, "MIDI %d-%d", card->number, device); 3611da177e4SLinus Torvalds } 3621da177e4SLinus Torvalds } 3631da177e4SLinus Torvalds if ((info->flags & SNDRV_RAWMIDI_INFO_OUTPUT) && p < output_count) 3641da177e4SLinus Torvalds port->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE; 3651da177e4SLinus Torvalds if ((info->flags & SNDRV_RAWMIDI_INFO_INPUT) && p < input_count) 3661da177e4SLinus Torvalds port->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ; 3671da177e4SLinus Torvalds if ((port->capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) && 3681da177e4SLinus Torvalds info->flags & SNDRV_RAWMIDI_INFO_DUPLEX) 3691da177e4SLinus Torvalds port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; 370450047a7SClemens Ladisch port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC 371450047a7SClemens Ladisch | SNDRV_SEQ_PORT_TYPE_HARDWARE 372450047a7SClemens Ladisch | SNDRV_SEQ_PORT_TYPE_PORT; 3731da177e4SLinus Torvalds port->midi_channels = 16; 3741da177e4SLinus Torvalds memset(&pcallbacks, 0, sizeof(pcallbacks)); 3751da177e4SLinus Torvalds pcallbacks.owner = THIS_MODULE; 3761da177e4SLinus Torvalds pcallbacks.private_data = ms; 3771da177e4SLinus Torvalds pcallbacks.subscribe = midisynth_subscribe; 3781da177e4SLinus Torvalds pcallbacks.unsubscribe = midisynth_unsubscribe; 3791da177e4SLinus Torvalds pcallbacks.use = midisynth_use; 3801da177e4SLinus Torvalds pcallbacks.unuse = midisynth_unuse; 3811da177e4SLinus Torvalds pcallbacks.event_input = event_process_midi; 3821da177e4SLinus Torvalds port->kernel = &pcallbacks; 383a7b928acSClemens Ladisch if (rmidi->ops && rmidi->ops->get_port_info) 384a7b928acSClemens Ladisch rmidi->ops->get_port_info(rmidi, p, port); 3851da177e4SLinus Torvalds if (snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_CREATE_PORT, port)<0) 3861da177e4SLinus Torvalds goto __nomem; 3871da177e4SLinus Torvalds ms->seq_client = client->seq_client; 3881da177e4SLinus Torvalds ms->seq_port = port->addr.port; 3891da177e4SLinus Torvalds } 3901da177e4SLinus Torvalds client->ports_per_device[device] = ports; 3911da177e4SLinus Torvalds client->ports[device] = msynth; 3921da177e4SLinus Torvalds client->num_ports++; 3931da177e4SLinus Torvalds if (newclient) 3941da177e4SLinus Torvalds synths[card->number] = client; 3951a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 39651f633daSTakashi Iwai kfree(info); 39751f633daSTakashi Iwai kfree(port); 3981da177e4SLinus Torvalds return 0; /* success */ 3991da177e4SLinus Torvalds 4001da177e4SLinus Torvalds __nomem: 4011da177e4SLinus Torvalds if (msynth != NULL) { 4021da177e4SLinus Torvalds for (p = 0; p < ports; p++) 4031da177e4SLinus Torvalds snd_seq_midisynth_delete(&msynth[p]); 4041da177e4SLinus Torvalds kfree(msynth); 4051da177e4SLinus Torvalds } 4061da177e4SLinus Torvalds if (newclient) { 4071da177e4SLinus Torvalds snd_seq_delete_kernel_client(client->seq_client); 4081da177e4SLinus Torvalds kfree(client); 4091da177e4SLinus Torvalds } 4101da177e4SLinus Torvalds kfree(info); 4111da177e4SLinus Torvalds kfree(port); 4121a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 4131da177e4SLinus Torvalds return -ENOMEM; 4141da177e4SLinus Torvalds } 4151da177e4SLinus Torvalds 4161da177e4SLinus Torvalds /* release midi synth port */ 4171da177e4SLinus Torvalds static int 41805662205STakashi Iwai snd_seq_midisynth_remove(struct device *_dev) 4191da177e4SLinus Torvalds { 42005662205STakashi Iwai struct snd_seq_device *dev = to_seq_dev(_dev); 421c7e0b5bfSTakashi Iwai struct seq_midisynth_client *client; 422c7e0b5bfSTakashi Iwai struct seq_midisynth *msynth; 423c7e0b5bfSTakashi Iwai struct snd_card *card = dev->card; 4241da177e4SLinus Torvalds int device = dev->device, p, ports; 4251da177e4SLinus Torvalds 4261a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 4271da177e4SLinus Torvalds client = synths[card->number]; 4281da177e4SLinus Torvalds if (client == NULL || client->ports[device] == NULL) { 4291a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 4301da177e4SLinus Torvalds return -ENODEV; 4311da177e4SLinus Torvalds } 4321da177e4SLinus Torvalds ports = client->ports_per_device[device]; 4331da177e4SLinus Torvalds client->ports_per_device[device] = 0; 4341da177e4SLinus Torvalds msynth = client->ports[device]; 4351da177e4SLinus Torvalds client->ports[device] = NULL; 4361da177e4SLinus Torvalds for (p = 0; p < ports; p++) 4371da177e4SLinus Torvalds snd_seq_midisynth_delete(&msynth[p]); 4381da177e4SLinus Torvalds kfree(msynth); 4391da177e4SLinus Torvalds client->num_ports--; 4401da177e4SLinus Torvalds if (client->num_ports <= 0) { 4411da177e4SLinus Torvalds snd_seq_delete_kernel_client(client->seq_client); 4421da177e4SLinus Torvalds synths[card->number] = NULL; 4431da177e4SLinus Torvalds kfree(client); 4441da177e4SLinus Torvalds } 4451a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 4461da177e4SLinus Torvalds return 0; 4471da177e4SLinus Torvalds } 4481da177e4SLinus Torvalds 44905662205STakashi Iwai static struct snd_seq_driver seq_midisynth_driver = { 45005662205STakashi Iwai .driver = { 45105662205STakashi Iwai .name = KBUILD_MODNAME, 45205662205STakashi Iwai .probe = snd_seq_midisynth_probe, 45305662205STakashi Iwai .remove = snd_seq_midisynth_remove, 45405662205STakashi Iwai }, 45505662205STakashi Iwai .id = SNDRV_SEQ_DEV_ID_MIDISYNTH, 45605662205STakashi Iwai .argsize = 0, 45705662205STakashi Iwai }; 4581da177e4SLinus Torvalds 45954a721abSTakashi Iwai module_snd_seq_driver(seq_midisynth_driver); 460