162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Midi Sequencer interface routines. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1999 Steve Ratcliffe 662306a36Sopenharmony_ci * Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "emux_voice.h" 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* Prototypes for static functions */ 1462306a36Sopenharmony_cistatic void free_port(void *private); 1562306a36Sopenharmony_cistatic void snd_emux_init_port(struct snd_emux_port *p); 1662306a36Sopenharmony_cistatic int snd_emux_use(void *private_data, struct snd_seq_port_subscribe *info); 1762306a36Sopenharmony_cistatic int snd_emux_unuse(void *private_data, struct snd_seq_port_subscribe *info); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * MIDI emulation operators 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_cistatic const struct snd_midi_op emux_ops = { 2362306a36Sopenharmony_ci .note_on = snd_emux_note_on, 2462306a36Sopenharmony_ci .note_off = snd_emux_note_off, 2562306a36Sopenharmony_ci .key_press = snd_emux_key_press, 2662306a36Sopenharmony_ci .note_terminate = snd_emux_terminate_note, 2762306a36Sopenharmony_ci .control = snd_emux_control, 2862306a36Sopenharmony_ci .nrpn = snd_emux_nrpn, 2962306a36Sopenharmony_ci .sysex = snd_emux_sysex, 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * number of MIDI channels 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci#define MIDI_CHANNELS 16 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * type flags for MIDI sequencer port 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci#define DEFAULT_MIDI_TYPE (SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |\ 4262306a36Sopenharmony_ci SNDRV_SEQ_PORT_TYPE_MIDI_GM |\ 4362306a36Sopenharmony_ci SNDRV_SEQ_PORT_TYPE_MIDI_GS |\ 4462306a36Sopenharmony_ci SNDRV_SEQ_PORT_TYPE_MIDI_XG |\ 4562306a36Sopenharmony_ci SNDRV_SEQ_PORT_TYPE_HARDWARE |\ 4662306a36Sopenharmony_ci SNDRV_SEQ_PORT_TYPE_SYNTHESIZER) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* 4962306a36Sopenharmony_ci * Initialise the EMUX Synth by creating a client and registering 5062306a36Sopenharmony_ci * a series of ports. 5162306a36Sopenharmony_ci * Each of the ports will contain the 16 midi channels. Applications 5262306a36Sopenharmony_ci * can connect to these ports to play midi data. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ciint 5562306a36Sopenharmony_cisnd_emux_init_seq(struct snd_emux *emu, struct snd_card *card, int index) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci int i; 5862306a36Sopenharmony_ci struct snd_seq_port_callback pinfo; 5962306a36Sopenharmony_ci char tmpname[64]; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci emu->client = snd_seq_create_kernel_client(card, index, 6262306a36Sopenharmony_ci "%s WaveTable", emu->name); 6362306a36Sopenharmony_ci if (emu->client < 0) { 6462306a36Sopenharmony_ci snd_printk(KERN_ERR "can't create client\n"); 6562306a36Sopenharmony_ci return -ENODEV; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (emu->num_ports < 0) { 6962306a36Sopenharmony_ci snd_printk(KERN_WARNING "seqports must be greater than zero\n"); 7062306a36Sopenharmony_ci emu->num_ports = 1; 7162306a36Sopenharmony_ci } else if (emu->num_ports >= SNDRV_EMUX_MAX_PORTS) { 7262306a36Sopenharmony_ci snd_printk(KERN_WARNING "too many ports." 7362306a36Sopenharmony_ci "limited max. ports %d\n", SNDRV_EMUX_MAX_PORTS); 7462306a36Sopenharmony_ci emu->num_ports = SNDRV_EMUX_MAX_PORTS; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci memset(&pinfo, 0, sizeof(pinfo)); 7862306a36Sopenharmony_ci pinfo.owner = THIS_MODULE; 7962306a36Sopenharmony_ci pinfo.use = snd_emux_use; 8062306a36Sopenharmony_ci pinfo.unuse = snd_emux_unuse; 8162306a36Sopenharmony_ci pinfo.event_input = snd_emux_event_input; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci for (i = 0; i < emu->num_ports; i++) { 8462306a36Sopenharmony_ci struct snd_emux_port *p; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci sprintf(tmpname, "%s Port %d", emu->name, i); 8762306a36Sopenharmony_ci p = snd_emux_create_port(emu, tmpname, MIDI_CHANNELS, 8862306a36Sopenharmony_ci 0, &pinfo); 8962306a36Sopenharmony_ci if (!p) { 9062306a36Sopenharmony_ci snd_printk(KERN_ERR "can't create port\n"); 9162306a36Sopenharmony_ci return -ENOMEM; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci p->port_mode = SNDRV_EMUX_PORT_MODE_MIDI; 9562306a36Sopenharmony_ci snd_emux_init_port(p); 9662306a36Sopenharmony_ci emu->ports[i] = p->chset.port; 9762306a36Sopenharmony_ci emu->portptrs[i] = p; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* 10562306a36Sopenharmony_ci * Detach from the ports that were set up for this synthesizer and 10662306a36Sopenharmony_ci * destroy the kernel client. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_civoid 10962306a36Sopenharmony_cisnd_emux_detach_seq(struct snd_emux *emu) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci if (emu->voices) 11262306a36Sopenharmony_ci snd_emux_terminate_all(emu); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (emu->client >= 0) { 11562306a36Sopenharmony_ci snd_seq_delete_kernel_client(emu->client); 11662306a36Sopenharmony_ci emu->client = -1; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* 12262306a36Sopenharmony_ci * create a sequencer port and channel_set 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistruct snd_emux_port * 12662306a36Sopenharmony_cisnd_emux_create_port(struct snd_emux *emu, char *name, 12762306a36Sopenharmony_ci int max_channels, int oss_port, 12862306a36Sopenharmony_ci struct snd_seq_port_callback *callback) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct snd_emux_port *p; 13162306a36Sopenharmony_ci int i, type, cap; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Allocate structures for this channel */ 13462306a36Sopenharmony_ci p = kzalloc(sizeof(*p), GFP_KERNEL); 13562306a36Sopenharmony_ci if (!p) 13662306a36Sopenharmony_ci return NULL; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci p->chset.channels = kcalloc(max_channels, sizeof(*p->chset.channels), 13962306a36Sopenharmony_ci GFP_KERNEL); 14062306a36Sopenharmony_ci if (!p->chset.channels) { 14162306a36Sopenharmony_ci kfree(p); 14262306a36Sopenharmony_ci return NULL; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci for (i = 0; i < max_channels; i++) 14562306a36Sopenharmony_ci p->chset.channels[i].number = i; 14662306a36Sopenharmony_ci p->chset.private_data = p; 14762306a36Sopenharmony_ci p->chset.max_channels = max_channels; 14862306a36Sopenharmony_ci p->emu = emu; 14962306a36Sopenharmony_ci p->chset.client = emu->client; 15062306a36Sopenharmony_ci#ifdef SNDRV_EMUX_USE_RAW_EFFECT 15162306a36Sopenharmony_ci snd_emux_create_effect(p); 15262306a36Sopenharmony_ci#endif 15362306a36Sopenharmony_ci callback->private_free = free_port; 15462306a36Sopenharmony_ci callback->private_data = p; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci cap = SNDRV_SEQ_PORT_CAP_WRITE; 15762306a36Sopenharmony_ci if (oss_port) { 15862306a36Sopenharmony_ci type = SNDRV_SEQ_PORT_TYPE_SPECIFIC; 15962306a36Sopenharmony_ci } else { 16062306a36Sopenharmony_ci type = DEFAULT_MIDI_TYPE; 16162306a36Sopenharmony_ci cap |= SNDRV_SEQ_PORT_CAP_SUBS_WRITE; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci p->chset.port = snd_seq_event_port_attach(emu->client, callback, 16562306a36Sopenharmony_ci cap, type, max_channels, 16662306a36Sopenharmony_ci emu->max_voices, name); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return p; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/* 17362306a36Sopenharmony_ci * release memory block for port 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_cistatic void 17662306a36Sopenharmony_cifree_port(void *private_data) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct snd_emux_port *p; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci p = private_data; 18162306a36Sopenharmony_ci if (p) { 18262306a36Sopenharmony_ci#ifdef SNDRV_EMUX_USE_RAW_EFFECT 18362306a36Sopenharmony_ci snd_emux_delete_effect(p); 18462306a36Sopenharmony_ci#endif 18562306a36Sopenharmony_ci kfree(p->chset.channels); 18662306a36Sopenharmony_ci kfree(p); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci#define DEFAULT_DRUM_FLAGS (1<<9) 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* 19462306a36Sopenharmony_ci * initialize the port specific parameters 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_cistatic void 19762306a36Sopenharmony_cisnd_emux_init_port(struct snd_emux_port *p) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci p->drum_flags = DEFAULT_DRUM_FLAGS; 20062306a36Sopenharmony_ci p->volume_atten = 0; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci snd_emux_reset_port(p); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/* 20762306a36Sopenharmony_ci * reset port 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_civoid 21062306a36Sopenharmony_cisnd_emux_reset_port(struct snd_emux_port *port) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci int i; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* stop all sounds */ 21562306a36Sopenharmony_ci snd_emux_sounds_off_all(port); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci snd_midi_channel_set_clear(&port->chset); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci#ifdef SNDRV_EMUX_USE_RAW_EFFECT 22062306a36Sopenharmony_ci snd_emux_clear_effect(port); 22162306a36Sopenharmony_ci#endif 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* set port specific control parameters */ 22462306a36Sopenharmony_ci port->ctrls[EMUX_MD_DEF_BANK] = 0; 22562306a36Sopenharmony_ci port->ctrls[EMUX_MD_DEF_DRUM] = 0; 22662306a36Sopenharmony_ci port->ctrls[EMUX_MD_REALTIME_PAN] = 1; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci for (i = 0; i < port->chset.max_channels; i++) { 22962306a36Sopenharmony_ci struct snd_midi_channel *chan = port->chset.channels + i; 23062306a36Sopenharmony_ci chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/* 23662306a36Sopenharmony_ci * input sequencer event 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ciint 23962306a36Sopenharmony_cisnd_emux_event_input(struct snd_seq_event *ev, int direct, void *private_data, 24062306a36Sopenharmony_ci int atomic, int hop) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct snd_emux_port *port; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci port = private_data; 24562306a36Sopenharmony_ci if (snd_BUG_ON(!port || !ev)) 24662306a36Sopenharmony_ci return -EINVAL; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci snd_midi_process_event(&emux_ops, ev, &port->chset); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/* 25562306a36Sopenharmony_ci * increment usage count 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_cistatic int 25862306a36Sopenharmony_ci__snd_emux_inc_count(struct snd_emux *emu) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci emu->used++; 26162306a36Sopenharmony_ci if (!try_module_get(emu->ops.owner)) 26262306a36Sopenharmony_ci goto __error; 26362306a36Sopenharmony_ci if (!try_module_get(emu->card->module)) { 26462306a36Sopenharmony_ci module_put(emu->ops.owner); 26562306a36Sopenharmony_ci __error: 26662306a36Sopenharmony_ci emu->used--; 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci return 1; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ciint snd_emux_inc_count(struct snd_emux *emu) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci int ret; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci mutex_lock(&emu->register_mutex); 27762306a36Sopenharmony_ci ret = __snd_emux_inc_count(emu); 27862306a36Sopenharmony_ci mutex_unlock(&emu->register_mutex); 27962306a36Sopenharmony_ci return ret; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci/* 28362306a36Sopenharmony_ci * decrease usage count 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_cistatic void 28662306a36Sopenharmony_ci__snd_emux_dec_count(struct snd_emux *emu) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci module_put(emu->card->module); 28962306a36Sopenharmony_ci emu->used--; 29062306a36Sopenharmony_ci if (emu->used <= 0) 29162306a36Sopenharmony_ci snd_emux_terminate_all(emu); 29262306a36Sopenharmony_ci module_put(emu->ops.owner); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_civoid snd_emux_dec_count(struct snd_emux *emu) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci mutex_lock(&emu->register_mutex); 29862306a36Sopenharmony_ci __snd_emux_dec_count(emu); 29962306a36Sopenharmony_ci mutex_unlock(&emu->register_mutex); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci/* 30362306a36Sopenharmony_ci * Routine that is called upon a first use of a particular port 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_cistatic int 30662306a36Sopenharmony_cisnd_emux_use(void *private_data, struct snd_seq_port_subscribe *info) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct snd_emux_port *p; 30962306a36Sopenharmony_ci struct snd_emux *emu; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci p = private_data; 31262306a36Sopenharmony_ci if (snd_BUG_ON(!p)) 31362306a36Sopenharmony_ci return -EINVAL; 31462306a36Sopenharmony_ci emu = p->emu; 31562306a36Sopenharmony_ci if (snd_BUG_ON(!emu)) 31662306a36Sopenharmony_ci return -EINVAL; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci mutex_lock(&emu->register_mutex); 31962306a36Sopenharmony_ci snd_emux_init_port(p); 32062306a36Sopenharmony_ci __snd_emux_inc_count(emu); 32162306a36Sopenharmony_ci mutex_unlock(&emu->register_mutex); 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci/* 32662306a36Sopenharmony_ci * Routine that is called upon the last unuse() of a particular port. 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_cistatic int 32962306a36Sopenharmony_cisnd_emux_unuse(void *private_data, struct snd_seq_port_subscribe *info) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct snd_emux_port *p; 33262306a36Sopenharmony_ci struct snd_emux *emu; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci p = private_data; 33562306a36Sopenharmony_ci if (snd_BUG_ON(!p)) 33662306a36Sopenharmony_ci return -EINVAL; 33762306a36Sopenharmony_ci emu = p->emu; 33862306a36Sopenharmony_ci if (snd_BUG_ON(!emu)) 33962306a36Sopenharmony_ci return -EINVAL; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci mutex_lock(&emu->register_mutex); 34262306a36Sopenharmony_ci snd_emux_sounds_off_all(p); 34362306a36Sopenharmony_ci __snd_emux_dec_count(emu); 34462306a36Sopenharmony_ci mutex_unlock(&emu->register_mutex); 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci/* 35062306a36Sopenharmony_ci * attach virtual rawmidi devices 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_ciint snd_emux_init_virmidi(struct snd_emux *emu, struct snd_card *card) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci int i; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci emu->vmidi = NULL; 35762306a36Sopenharmony_ci if (emu->midi_ports <= 0) 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci emu->vmidi = kcalloc(emu->midi_ports, sizeof(*emu->vmidi), GFP_KERNEL); 36162306a36Sopenharmony_ci if (!emu->vmidi) 36262306a36Sopenharmony_ci return -ENOMEM; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci for (i = 0; i < emu->midi_ports; i++) { 36562306a36Sopenharmony_ci struct snd_rawmidi *rmidi; 36662306a36Sopenharmony_ci struct snd_virmidi_dev *rdev; 36762306a36Sopenharmony_ci if (snd_virmidi_new(card, emu->midi_devidx + i, &rmidi) < 0) 36862306a36Sopenharmony_ci goto __error; 36962306a36Sopenharmony_ci rdev = rmidi->private_data; 37062306a36Sopenharmony_ci sprintf(rmidi->name, "%s Synth MIDI", emu->name); 37162306a36Sopenharmony_ci rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH; 37262306a36Sopenharmony_ci rdev->client = emu->client; 37362306a36Sopenharmony_ci rdev->port = emu->ports[i]; 37462306a36Sopenharmony_ci if (snd_device_register(card, rmidi) < 0) { 37562306a36Sopenharmony_ci snd_device_free(card, rmidi); 37662306a36Sopenharmony_ci goto __error; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci emu->vmidi[i] = rmidi; 37962306a36Sopenharmony_ci /* snd_printk(KERN_DEBUG "virmidi %d ok\n", i); */ 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci__error: 38462306a36Sopenharmony_ci /* snd_printk(KERN_DEBUG "error init..\n"); */ 38562306a36Sopenharmony_ci snd_emux_delete_virmidi(emu); 38662306a36Sopenharmony_ci return -ENOMEM; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ciint snd_emux_delete_virmidi(struct snd_emux *emu) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci int i; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (!emu->vmidi) 39462306a36Sopenharmony_ci return 0; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci for (i = 0; i < emu->midi_ports; i++) { 39762306a36Sopenharmony_ci if (emu->vmidi[i]) 39862306a36Sopenharmony_ci snd_device_free(emu->card, emu->vmidi[i]); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci kfree(emu->vmidi); 40162306a36Sopenharmony_ci emu->vmidi = NULL; 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci} 404