18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Midi Sequencer interface routines. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1999 Steve Ratcliffe 68c2ecf20Sopenharmony_ci * Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "emux_voice.h" 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* Prototypes for static functions */ 148c2ecf20Sopenharmony_cistatic void free_port(void *private); 158c2ecf20Sopenharmony_cistatic void snd_emux_init_port(struct snd_emux_port *p); 168c2ecf20Sopenharmony_cistatic int snd_emux_use(void *private_data, struct snd_seq_port_subscribe *info); 178c2ecf20Sopenharmony_cistatic int snd_emux_unuse(void *private_data, struct snd_seq_port_subscribe *info); 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * MIDI emulation operators 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_cistatic const struct snd_midi_op emux_ops = { 238c2ecf20Sopenharmony_ci .note_on = snd_emux_note_on, 248c2ecf20Sopenharmony_ci .note_off = snd_emux_note_off, 258c2ecf20Sopenharmony_ci .key_press = snd_emux_key_press, 268c2ecf20Sopenharmony_ci .note_terminate = snd_emux_terminate_note, 278c2ecf20Sopenharmony_ci .control = snd_emux_control, 288c2ecf20Sopenharmony_ci .nrpn = snd_emux_nrpn, 298c2ecf20Sopenharmony_ci .sysex = snd_emux_sysex, 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * number of MIDI channels 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci#define MIDI_CHANNELS 16 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * type flags for MIDI sequencer port 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci#define DEFAULT_MIDI_TYPE (SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |\ 428c2ecf20Sopenharmony_ci SNDRV_SEQ_PORT_TYPE_MIDI_GM |\ 438c2ecf20Sopenharmony_ci SNDRV_SEQ_PORT_TYPE_MIDI_GS |\ 448c2ecf20Sopenharmony_ci SNDRV_SEQ_PORT_TYPE_MIDI_XG |\ 458c2ecf20Sopenharmony_ci SNDRV_SEQ_PORT_TYPE_HARDWARE |\ 468c2ecf20Sopenharmony_ci SNDRV_SEQ_PORT_TYPE_SYNTHESIZER) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * Initialise the EMUX Synth by creating a client and registering 508c2ecf20Sopenharmony_ci * a series of ports. 518c2ecf20Sopenharmony_ci * Each of the ports will contain the 16 midi channels. Applications 528c2ecf20Sopenharmony_ci * can connect to these ports to play midi data. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ciint 558c2ecf20Sopenharmony_cisnd_emux_init_seq(struct snd_emux *emu, struct snd_card *card, int index) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci int i; 588c2ecf20Sopenharmony_ci struct snd_seq_port_callback pinfo; 598c2ecf20Sopenharmony_ci char tmpname[64]; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci emu->client = snd_seq_create_kernel_client(card, index, 628c2ecf20Sopenharmony_ci "%s WaveTable", emu->name); 638c2ecf20Sopenharmony_ci if (emu->client < 0) { 648c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "can't create client\n"); 658c2ecf20Sopenharmony_ci return -ENODEV; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (emu->num_ports < 0) { 698c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING "seqports must be greater than zero\n"); 708c2ecf20Sopenharmony_ci emu->num_ports = 1; 718c2ecf20Sopenharmony_ci } else if (emu->num_ports >= SNDRV_EMUX_MAX_PORTS) { 728c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING "too many ports." 738c2ecf20Sopenharmony_ci "limited max. ports %d\n", SNDRV_EMUX_MAX_PORTS); 748c2ecf20Sopenharmony_ci emu->num_ports = SNDRV_EMUX_MAX_PORTS; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci memset(&pinfo, 0, sizeof(pinfo)); 788c2ecf20Sopenharmony_ci pinfo.owner = THIS_MODULE; 798c2ecf20Sopenharmony_ci pinfo.use = snd_emux_use; 808c2ecf20Sopenharmony_ci pinfo.unuse = snd_emux_unuse; 818c2ecf20Sopenharmony_ci pinfo.event_input = snd_emux_event_input; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci for (i = 0; i < emu->num_ports; i++) { 848c2ecf20Sopenharmony_ci struct snd_emux_port *p; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci sprintf(tmpname, "%s Port %d", emu->name, i); 878c2ecf20Sopenharmony_ci p = snd_emux_create_port(emu, tmpname, MIDI_CHANNELS, 888c2ecf20Sopenharmony_ci 0, &pinfo); 898c2ecf20Sopenharmony_ci if (!p) { 908c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "can't create port\n"); 918c2ecf20Sopenharmony_ci return -ENOMEM; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci p->port_mode = SNDRV_EMUX_PORT_MODE_MIDI; 958c2ecf20Sopenharmony_ci snd_emux_init_port(p); 968c2ecf20Sopenharmony_ci emu->ports[i] = p->chset.port; 978c2ecf20Sopenharmony_ci emu->portptrs[i] = p; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * Detach from the ports that were set up for this synthesizer and 1068c2ecf20Sopenharmony_ci * destroy the kernel client. 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_civoid 1098c2ecf20Sopenharmony_cisnd_emux_detach_seq(struct snd_emux *emu) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci if (emu->voices) 1128c2ecf20Sopenharmony_ci snd_emux_terminate_all(emu); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (emu->client >= 0) { 1158c2ecf20Sopenharmony_ci snd_seq_delete_kernel_client(emu->client); 1168c2ecf20Sopenharmony_ci emu->client = -1; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* 1228c2ecf20Sopenharmony_ci * create a sequencer port and channel_set 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistruct snd_emux_port * 1268c2ecf20Sopenharmony_cisnd_emux_create_port(struct snd_emux *emu, char *name, 1278c2ecf20Sopenharmony_ci int max_channels, int oss_port, 1288c2ecf20Sopenharmony_ci struct snd_seq_port_callback *callback) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct snd_emux_port *p; 1318c2ecf20Sopenharmony_ci int i, type, cap; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* Allocate structures for this channel */ 1348c2ecf20Sopenharmony_ci p = kzalloc(sizeof(*p), GFP_KERNEL); 1358c2ecf20Sopenharmony_ci if (!p) 1368c2ecf20Sopenharmony_ci return NULL; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci p->chset.channels = kcalloc(max_channels, sizeof(*p->chset.channels), 1398c2ecf20Sopenharmony_ci GFP_KERNEL); 1408c2ecf20Sopenharmony_ci if (!p->chset.channels) { 1418c2ecf20Sopenharmony_ci kfree(p); 1428c2ecf20Sopenharmony_ci return NULL; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci for (i = 0; i < max_channels; i++) 1458c2ecf20Sopenharmony_ci p->chset.channels[i].number = i; 1468c2ecf20Sopenharmony_ci p->chset.private_data = p; 1478c2ecf20Sopenharmony_ci p->chset.max_channels = max_channels; 1488c2ecf20Sopenharmony_ci p->emu = emu; 1498c2ecf20Sopenharmony_ci p->chset.client = emu->client; 1508c2ecf20Sopenharmony_ci#ifdef SNDRV_EMUX_USE_RAW_EFFECT 1518c2ecf20Sopenharmony_ci snd_emux_create_effect(p); 1528c2ecf20Sopenharmony_ci#endif 1538c2ecf20Sopenharmony_ci callback->private_free = free_port; 1548c2ecf20Sopenharmony_ci callback->private_data = p; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci cap = SNDRV_SEQ_PORT_CAP_WRITE; 1578c2ecf20Sopenharmony_ci if (oss_port) { 1588c2ecf20Sopenharmony_ci type = SNDRV_SEQ_PORT_TYPE_SPECIFIC; 1598c2ecf20Sopenharmony_ci } else { 1608c2ecf20Sopenharmony_ci type = DEFAULT_MIDI_TYPE; 1618c2ecf20Sopenharmony_ci cap |= SNDRV_SEQ_PORT_CAP_SUBS_WRITE; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci p->chset.port = snd_seq_event_port_attach(emu->client, callback, 1658c2ecf20Sopenharmony_ci cap, type, max_channels, 1668c2ecf20Sopenharmony_ci emu->max_voices, name); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return p; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/* 1738c2ecf20Sopenharmony_ci * release memory block for port 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_cistatic void 1768c2ecf20Sopenharmony_cifree_port(void *private_data) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct snd_emux_port *p; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci p = private_data; 1818c2ecf20Sopenharmony_ci if (p) { 1828c2ecf20Sopenharmony_ci#ifdef SNDRV_EMUX_USE_RAW_EFFECT 1838c2ecf20Sopenharmony_ci snd_emux_delete_effect(p); 1848c2ecf20Sopenharmony_ci#endif 1858c2ecf20Sopenharmony_ci kfree(p->chset.channels); 1868c2ecf20Sopenharmony_ci kfree(p); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci#define DEFAULT_DRUM_FLAGS (1<<9) 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci/* 1948c2ecf20Sopenharmony_ci * initialize the port specific parameters 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_cistatic void 1978c2ecf20Sopenharmony_cisnd_emux_init_port(struct snd_emux_port *p) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci p->drum_flags = DEFAULT_DRUM_FLAGS; 2008c2ecf20Sopenharmony_ci p->volume_atten = 0; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci snd_emux_reset_port(p); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* 2078c2ecf20Sopenharmony_ci * reset port 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_civoid 2108c2ecf20Sopenharmony_cisnd_emux_reset_port(struct snd_emux_port *port) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci int i; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* stop all sounds */ 2158c2ecf20Sopenharmony_ci snd_emux_sounds_off_all(port); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci snd_midi_channel_set_clear(&port->chset); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci#ifdef SNDRV_EMUX_USE_RAW_EFFECT 2208c2ecf20Sopenharmony_ci snd_emux_clear_effect(port); 2218c2ecf20Sopenharmony_ci#endif 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* set port specific control parameters */ 2248c2ecf20Sopenharmony_ci port->ctrls[EMUX_MD_DEF_BANK] = 0; 2258c2ecf20Sopenharmony_ci port->ctrls[EMUX_MD_DEF_DRUM] = 0; 2268c2ecf20Sopenharmony_ci port->ctrls[EMUX_MD_REALTIME_PAN] = 1; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci for (i = 0; i < port->chset.max_channels; i++) { 2298c2ecf20Sopenharmony_ci struct snd_midi_channel *chan = port->chset.channels + i; 2308c2ecf20Sopenharmony_ci chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* 2368c2ecf20Sopenharmony_ci * input sequencer event 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ciint 2398c2ecf20Sopenharmony_cisnd_emux_event_input(struct snd_seq_event *ev, int direct, void *private_data, 2408c2ecf20Sopenharmony_ci int atomic, int hop) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct snd_emux_port *port; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci port = private_data; 2458c2ecf20Sopenharmony_ci if (snd_BUG_ON(!port || !ev)) 2468c2ecf20Sopenharmony_ci return -EINVAL; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci snd_midi_process_event(&emux_ops, ev, &port->chset); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* 2558c2ecf20Sopenharmony_ci * increment usage count 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_cistatic int 2588c2ecf20Sopenharmony_ci__snd_emux_inc_count(struct snd_emux *emu) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci emu->used++; 2618c2ecf20Sopenharmony_ci if (!try_module_get(emu->ops.owner)) 2628c2ecf20Sopenharmony_ci goto __error; 2638c2ecf20Sopenharmony_ci if (!try_module_get(emu->card->module)) { 2648c2ecf20Sopenharmony_ci module_put(emu->ops.owner); 2658c2ecf20Sopenharmony_ci __error: 2668c2ecf20Sopenharmony_ci emu->used--; 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci return 1; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ciint snd_emux_inc_count(struct snd_emux *emu) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci int ret; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci mutex_lock(&emu->register_mutex); 2778c2ecf20Sopenharmony_ci ret = __snd_emux_inc_count(emu); 2788c2ecf20Sopenharmony_ci mutex_unlock(&emu->register_mutex); 2798c2ecf20Sopenharmony_ci return ret; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci/* 2838c2ecf20Sopenharmony_ci * decrease usage count 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_cistatic void 2868c2ecf20Sopenharmony_ci__snd_emux_dec_count(struct snd_emux *emu) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci module_put(emu->card->module); 2898c2ecf20Sopenharmony_ci emu->used--; 2908c2ecf20Sopenharmony_ci if (emu->used <= 0) 2918c2ecf20Sopenharmony_ci snd_emux_terminate_all(emu); 2928c2ecf20Sopenharmony_ci module_put(emu->ops.owner); 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_civoid snd_emux_dec_count(struct snd_emux *emu) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci mutex_lock(&emu->register_mutex); 2988c2ecf20Sopenharmony_ci __snd_emux_dec_count(emu); 2998c2ecf20Sopenharmony_ci mutex_unlock(&emu->register_mutex); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/* 3038c2ecf20Sopenharmony_ci * Routine that is called upon a first use of a particular port 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_cistatic int 3068c2ecf20Sopenharmony_cisnd_emux_use(void *private_data, struct snd_seq_port_subscribe *info) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct snd_emux_port *p; 3098c2ecf20Sopenharmony_ci struct snd_emux *emu; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci p = private_data; 3128c2ecf20Sopenharmony_ci if (snd_BUG_ON(!p)) 3138c2ecf20Sopenharmony_ci return -EINVAL; 3148c2ecf20Sopenharmony_ci emu = p->emu; 3158c2ecf20Sopenharmony_ci if (snd_BUG_ON(!emu)) 3168c2ecf20Sopenharmony_ci return -EINVAL; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci mutex_lock(&emu->register_mutex); 3198c2ecf20Sopenharmony_ci snd_emux_init_port(p); 3208c2ecf20Sopenharmony_ci __snd_emux_inc_count(emu); 3218c2ecf20Sopenharmony_ci mutex_unlock(&emu->register_mutex); 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci/* 3268c2ecf20Sopenharmony_ci * Routine that is called upon the last unuse() of a particular port. 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_cistatic int 3298c2ecf20Sopenharmony_cisnd_emux_unuse(void *private_data, struct snd_seq_port_subscribe *info) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct snd_emux_port *p; 3328c2ecf20Sopenharmony_ci struct snd_emux *emu; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci p = private_data; 3358c2ecf20Sopenharmony_ci if (snd_BUG_ON(!p)) 3368c2ecf20Sopenharmony_ci return -EINVAL; 3378c2ecf20Sopenharmony_ci emu = p->emu; 3388c2ecf20Sopenharmony_ci if (snd_BUG_ON(!emu)) 3398c2ecf20Sopenharmony_ci return -EINVAL; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci mutex_lock(&emu->register_mutex); 3428c2ecf20Sopenharmony_ci snd_emux_sounds_off_all(p); 3438c2ecf20Sopenharmony_ci __snd_emux_dec_count(emu); 3448c2ecf20Sopenharmony_ci mutex_unlock(&emu->register_mutex); 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci/* 3508c2ecf20Sopenharmony_ci * attach virtual rawmidi devices 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_ciint snd_emux_init_virmidi(struct snd_emux *emu, struct snd_card *card) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci int i; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci emu->vmidi = NULL; 3578c2ecf20Sopenharmony_ci if (emu->midi_ports <= 0) 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci emu->vmidi = kcalloc(emu->midi_ports, sizeof(*emu->vmidi), GFP_KERNEL); 3618c2ecf20Sopenharmony_ci if (!emu->vmidi) 3628c2ecf20Sopenharmony_ci return -ENOMEM; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci for (i = 0; i < emu->midi_ports; i++) { 3658c2ecf20Sopenharmony_ci struct snd_rawmidi *rmidi; 3668c2ecf20Sopenharmony_ci struct snd_virmidi_dev *rdev; 3678c2ecf20Sopenharmony_ci if (snd_virmidi_new(card, emu->midi_devidx + i, &rmidi) < 0) 3688c2ecf20Sopenharmony_ci goto __error; 3698c2ecf20Sopenharmony_ci rdev = rmidi->private_data; 3708c2ecf20Sopenharmony_ci sprintf(rmidi->name, "%s Synth MIDI", emu->name); 3718c2ecf20Sopenharmony_ci rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH; 3728c2ecf20Sopenharmony_ci rdev->client = emu->client; 3738c2ecf20Sopenharmony_ci rdev->port = emu->ports[i]; 3748c2ecf20Sopenharmony_ci if (snd_device_register(card, rmidi) < 0) { 3758c2ecf20Sopenharmony_ci snd_device_free(card, rmidi); 3768c2ecf20Sopenharmony_ci goto __error; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci emu->vmidi[i] = rmidi; 3798c2ecf20Sopenharmony_ci /* snd_printk(KERN_DEBUG "virmidi %d ok\n", i); */ 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci__error: 3848c2ecf20Sopenharmony_ci /* snd_printk(KERN_DEBUG "error init..\n"); */ 3858c2ecf20Sopenharmony_ci snd_emux_delete_virmidi(emu); 3868c2ecf20Sopenharmony_ci return -ENOMEM; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ciint snd_emux_delete_virmidi(struct snd_emux *emu) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci int i; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (!emu->vmidi) 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci for (i = 0; i < emu->midi_ports; i++) { 3978c2ecf20Sopenharmony_ci if (emu->vmidi[i]) 3988c2ecf20Sopenharmony_ci snd_device_free(emu->card, emu->vmidi[i]); 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci kfree(emu->vmidi); 4018c2ecf20Sopenharmony_ci emu->vmidi = NULL; 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci} 404