2005-04-17 02:20:36 +04:00
/*
* Midi Sequencer interface routines .
*
* Copyright ( C ) 1999 Steve Ratcliffe
* Copyright ( c ) 1999 - 2000 Takashi Iwai < tiwai @ suse . de >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include "emux_voice.h"
# include <linux/slab.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
/* Prototypes for static functions */
static void free_port ( void * private ) ;
2005-11-17 16:24:47 +03:00
static void snd_emux_init_port ( struct snd_emux_port * p ) ;
static int snd_emux_use ( void * private_data , struct snd_seq_port_subscribe * info ) ;
static int snd_emux_unuse ( void * private_data , struct snd_seq_port_subscribe * info ) ;
2005-04-17 02:20:36 +04:00
/*
* MIDI emulation operators
*/
2005-11-17 16:24:47 +03:00
static struct snd_midi_op emux_ops = {
2016-12-17 03:59:44 +03:00
. note_on = snd_emux_note_on ,
. note_off = snd_emux_note_off ,
. key_press = snd_emux_key_press ,
. note_terminate = snd_emux_terminate_note ,
. control = snd_emux_control ,
. nrpn = snd_emux_nrpn ,
. sysex = snd_emux_sysex ,
2005-04-17 02:20:36 +04:00
} ;
/*
* number of MIDI channels
*/
# define MIDI_CHANNELS 16
/*
* type flags for MIDI sequencer port
*/
# define DEFAULT_MIDI_TYPE (SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |\
SNDRV_SEQ_PORT_TYPE_MIDI_GM | \
SNDRV_SEQ_PORT_TYPE_MIDI_GS | \
2006-05-02 18:08:41 +04:00
SNDRV_SEQ_PORT_TYPE_MIDI_XG | \
SNDRV_SEQ_PORT_TYPE_HARDWARE | \
SNDRV_SEQ_PORT_TYPE_SYNTHESIZER )
2005-04-17 02:20:36 +04:00
/*
* Initialise the EMUX Synth by creating a client and registering
* a series of ports .
* Each of the ports will contain the 16 midi channels . Applications
* can connect to these ports to play midi data .
*/
int
2005-11-17 16:24:47 +03:00
snd_emux_init_seq ( struct snd_emux * emu , struct snd_card * card , int index )
2005-04-17 02:20:36 +04:00
{
int i ;
2005-11-17 16:24:47 +03:00
struct snd_seq_port_callback pinfo ;
2005-04-17 02:20:36 +04:00
char tmpname [ 64 ] ;
2005-12-12 11:33:37 +03:00
emu - > client = snd_seq_create_kernel_client ( card , index ,
" %s WaveTable " , emu - > name ) ;
2005-04-17 02:20:36 +04:00
if ( emu - > client < 0 ) {
2009-02-05 18:01:46 +03:00
snd_printk ( KERN_ERR " can't create client \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
if ( emu - > num_ports < 0 ) {
2009-02-05 18:01:46 +03:00
snd_printk ( KERN_WARNING " seqports must be greater than zero \n " ) ;
2005-04-17 02:20:36 +04:00
emu - > num_ports = 1 ;
} else if ( emu - > num_ports > = SNDRV_EMUX_MAX_PORTS ) {
2009-02-05 18:01:46 +03:00
snd_printk ( KERN_WARNING " too many ports. "
2005-04-17 02:20:36 +04:00
" limited max. ports %d \n " , SNDRV_EMUX_MAX_PORTS ) ;
emu - > num_ports = SNDRV_EMUX_MAX_PORTS ;
}
memset ( & pinfo , 0 , sizeof ( pinfo ) ) ;
pinfo . owner = THIS_MODULE ;
pinfo . use = snd_emux_use ;
pinfo . unuse = snd_emux_unuse ;
pinfo . event_input = snd_emux_event_input ;
for ( i = 0 ; i < emu - > num_ports ; i + + ) {
2005-11-17 16:24:47 +03:00
struct snd_emux_port * p ;
2005-04-17 02:20:36 +04:00
sprintf ( tmpname , " %s Port %d " , emu - > name , i ) ;
p = snd_emux_create_port ( emu , tmpname , MIDI_CHANNELS ,
0 , & pinfo ) ;
2017-08-09 10:22:42 +03:00
if ( ! p ) {
2009-02-05 18:01:46 +03:00
snd_printk ( KERN_ERR " can't create port \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
p - > port_mode = SNDRV_EMUX_PORT_MODE_MIDI ;
snd_emux_init_port ( p ) ;
emu - > ports [ i ] = p - > chset . port ;
emu - > portptrs [ i ] = p ;
}
return 0 ;
}
/*
* Detach from the ports that were set up for this synthesizer and
* destroy the kernel client .
*/
void
2005-11-17 16:24:47 +03:00
snd_emux_detach_seq ( struct snd_emux * emu )
2005-04-17 02:20:36 +04:00
{
if ( emu - > voices )
snd_emux_terminate_all ( emu ) ;
if ( emu - > client > = 0 ) {
snd_seq_delete_kernel_client ( emu - > client ) ;
emu - > client = - 1 ;
}
}
/*
* create a sequencer port and channel_set
*/
2005-11-17 16:24:47 +03:00
struct snd_emux_port *
snd_emux_create_port ( struct snd_emux * emu , char * name ,
int max_channels , int oss_port ,
struct snd_seq_port_callback * callback )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:24:47 +03:00
struct snd_emux_port * p ;
2005-04-17 02:20:36 +04:00
int i , type , cap ;
/* Allocate structures for this channel */
2017-08-09 09:40:14 +03:00
p = kzalloc ( sizeof ( * p ) , GFP_KERNEL ) ;
2017-08-09 10:30:34 +03:00
if ( ! p )
2005-04-17 02:20:36 +04:00
return NULL ;
2017-08-09 10:30:34 +03:00
2017-08-09 10:11:26 +03:00
p - > chset . channels = kcalloc ( max_channels , sizeof ( * p - > chset . channels ) ,
GFP_KERNEL ) ;
2017-08-09 10:22:42 +03:00
if ( ! p - > chset . channels ) {
2005-04-17 02:20:36 +04:00
kfree ( p ) ;
return NULL ;
}
for ( i = 0 ; i < max_channels ; i + + )
p - > chset . channels [ i ] . number = i ;
p - > chset . private_data = p ;
p - > chset . max_channels = max_channels ;
p - > emu = emu ;
p - > chset . client = emu - > client ;
# ifdef SNDRV_EMUX_USE_RAW_EFFECT
snd_emux_create_effect ( p ) ;
# endif
callback - > private_free = free_port ;
callback - > private_data = p ;
cap = SNDRV_SEQ_PORT_CAP_WRITE ;
if ( oss_port ) {
type = SNDRV_SEQ_PORT_TYPE_SPECIFIC ;
} else {
type = DEFAULT_MIDI_TYPE ;
cap | = SNDRV_SEQ_PORT_CAP_SUBS_WRITE ;
}
p - > chset . port = snd_seq_event_port_attach ( emu - > client , callback ,
cap , type , max_channels ,
emu - > max_voices , name ) ;
return p ;
}
/*
* release memory block for port
*/
static void
free_port ( void * private_data )
{
2005-11-17 16:24:47 +03:00
struct snd_emux_port * p ;
2005-04-17 02:20:36 +04:00
p = private_data ;
if ( p ) {
# ifdef SNDRV_EMUX_USE_RAW_EFFECT
snd_emux_delete_effect ( p ) ;
# endif
kfree ( p - > chset . channels ) ;
kfree ( p ) ;
}
}
# define DEFAULT_DRUM_FLAGS (1<<9)
/*
* initialize the port specific parameters
*/
static void
2005-11-17 16:24:47 +03:00
snd_emux_init_port ( struct snd_emux_port * p )
2005-04-17 02:20:36 +04:00
{
p - > drum_flags = DEFAULT_DRUM_FLAGS ;
p - > volume_atten = 0 ;
snd_emux_reset_port ( p ) ;
}
/*
* reset port
*/
void
2005-11-17 16:24:47 +03:00
snd_emux_reset_port ( struct snd_emux_port * port )
2005-04-17 02:20:36 +04:00
{
int i ;
/* stop all sounds */
snd_emux_sounds_off_all ( port ) ;
snd_midi_channel_set_clear ( & port - > chset ) ;
# ifdef SNDRV_EMUX_USE_RAW_EFFECT
snd_emux_clear_effect ( port ) ;
# endif
/* set port specific control parameters */
port - > ctrls [ EMUX_MD_DEF_BANK ] = 0 ;
port - > ctrls [ EMUX_MD_DEF_DRUM ] = 0 ;
port - > ctrls [ EMUX_MD_REALTIME_PAN ] = 1 ;
for ( i = 0 ; i < port - > chset . max_channels ; i + + ) {
2005-11-17 16:24:47 +03:00
struct snd_midi_channel * chan = port - > chset . channels + i ;
2005-04-17 02:20:36 +04:00
chan - > drum_channel = ( ( port - > drum_flags > > i ) & 1 ) ? 1 : 0 ;
}
}
/*
* input sequencer event
*/
int
2005-11-17 16:24:47 +03:00
snd_emux_event_input ( struct snd_seq_event * ev , int direct , void * private_data ,
2005-04-17 02:20:36 +04:00
int atomic , int hop )
{
2005-11-17 16:24:47 +03:00
struct snd_emux_port * port ;
2005-04-17 02:20:36 +04:00
port = private_data ;
2008-08-08 19:12:47 +04:00
if ( snd_BUG_ON ( ! port | | ! ev ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
snd_midi_process_event ( & emux_ops , ev , & port - > chset ) ;
return 0 ;
}
/*
* increment usage count
*/
2015-04-28 18:11:44 +03:00
static int
__snd_emux_inc_count ( struct snd_emux * emu )
2005-04-17 02:20:36 +04:00
{
emu - > used + + ;
if ( ! try_module_get ( emu - > ops . owner ) )
goto __error ;
if ( ! try_module_get ( emu - > card - > module ) ) {
module_put ( emu - > ops . owner ) ;
__error :
emu - > used - - ;
return 0 ;
}
return 1 ;
}
2015-04-28 18:11:44 +03:00
int snd_emux_inc_count ( struct snd_emux * emu )
{
int ret ;
mutex_lock ( & emu - > register_mutex ) ;
ret = __snd_emux_inc_count ( emu ) ;
mutex_unlock ( & emu - > register_mutex ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
/*
* decrease usage count
*/
2015-04-28 18:11:44 +03:00
static void
__snd_emux_dec_count ( struct snd_emux * emu )
2005-04-17 02:20:36 +04:00
{
module_put ( emu - > card - > module ) ;
emu - > used - - ;
if ( emu - > used < = 0 )
snd_emux_terminate_all ( emu ) ;
module_put ( emu - > ops . owner ) ;
}
2015-04-28 18:11:44 +03:00
void snd_emux_dec_count ( struct snd_emux * emu )
{
mutex_lock ( & emu - > register_mutex ) ;
__snd_emux_dec_count ( emu ) ;
mutex_unlock ( & emu - > register_mutex ) ;
}
2005-04-17 02:20:36 +04:00
/*
* Routine that is called upon a first use of a particular port
*/
static int
2005-11-17 16:24:47 +03:00
snd_emux_use ( void * private_data , struct snd_seq_port_subscribe * info )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:24:47 +03:00
struct snd_emux_port * p ;
struct snd_emux * emu ;
2005-04-17 02:20:36 +04:00
p = private_data ;
2008-08-08 19:12:47 +04:00
if ( snd_BUG_ON ( ! p ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
emu = p - > emu ;
2008-08-08 19:12:47 +04:00
if ( snd_BUG_ON ( ! emu ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-01-16 18:31:42 +03:00
mutex_lock ( & emu - > register_mutex ) ;
2005-04-17 02:20:36 +04:00
snd_emux_init_port ( p ) ;
2015-04-28 18:11:44 +03:00
__snd_emux_inc_count ( emu ) ;
2006-01-16 18:31:42 +03:00
mutex_unlock ( & emu - > register_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Routine that is called upon the last unuse ( ) of a particular port .
*/
static int
2005-11-17 16:24:47 +03:00
snd_emux_unuse ( void * private_data , struct snd_seq_port_subscribe * info )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:24:47 +03:00
struct snd_emux_port * p ;
struct snd_emux * emu ;
2005-04-17 02:20:36 +04:00
p = private_data ;
2008-08-08 19:12:47 +04:00
if ( snd_BUG_ON ( ! p ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
emu = p - > emu ;
2008-08-08 19:12:47 +04:00
if ( snd_BUG_ON ( ! emu ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-01-16 18:31:42 +03:00
mutex_lock ( & emu - > register_mutex ) ;
2005-04-17 02:20:36 +04:00
snd_emux_sounds_off_all ( p ) ;
2015-04-28 18:11:44 +03:00
__snd_emux_dec_count ( emu ) ;
2006-01-16 18:31:42 +03:00
mutex_unlock ( & emu - > register_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* attach virtual rawmidi devices
*/
2005-11-17 16:24:47 +03:00
int snd_emux_init_virmidi ( struct snd_emux * emu , struct snd_card * card )
2005-04-17 02:20:36 +04:00
{
int i ;
emu - > vmidi = NULL ;
if ( emu - > midi_ports < = 0 )
return 0 ;
2017-08-09 10:11:26 +03:00
emu - > vmidi = kcalloc ( emu - > midi_ports , sizeof ( * emu - > vmidi ) , GFP_KERNEL ) ;
2017-08-09 10:22:42 +03:00
if ( ! emu - > vmidi )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
for ( i = 0 ; i < emu - > midi_ports ; i + + ) {
2005-11-17 16:24:47 +03:00
struct snd_rawmidi * rmidi ;
struct snd_virmidi_dev * rdev ;
2005-04-17 02:20:36 +04:00
if ( snd_virmidi_new ( card , emu - > midi_devidx + i , & rmidi ) < 0 )
goto __error ;
rdev = rmidi - > private_data ;
sprintf ( rmidi - > name , " %s Synth MIDI " , emu - > name ) ;
rdev - > seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH ;
rdev - > client = emu - > client ;
rdev - > port = emu - > ports [ i ] ;
if ( snd_device_register ( card , rmidi ) < 0 ) {
snd_device_free ( card , rmidi ) ;
goto __error ;
}
emu - > vmidi [ i ] = rmidi ;
2009-02-05 18:01:46 +03:00
/* snd_printk(KERN_DEBUG "virmidi %d ok\n", i); */
2005-04-17 02:20:36 +04:00
}
return 0 ;
__error :
2009-02-05 18:01:46 +03:00
/* snd_printk(KERN_DEBUG "error init..\n"); */
2005-04-17 02:20:36 +04:00
snd_emux_delete_virmidi ( emu ) ;
return - ENOMEM ;
}
2005-11-17 16:24:47 +03:00
int snd_emux_delete_virmidi ( struct snd_emux * emu )
2005-04-17 02:20:36 +04:00
{
int i ;
2017-08-09 10:22:42 +03:00
if ( ! emu - > vmidi )
2005-04-17 02:20:36 +04:00
return 0 ;
for ( i = 0 ; i < emu - > midi_ports ; i + + ) {
if ( emu - > vmidi [ i ] )
snd_device_free ( emu - > card , emu - > vmidi [ i ] ) ;
}
kfree ( emu - > vmidi ) ;
emu - > vmidi = NULL ;
return 0 ;
}