2005-04-17 02:20:36 +04:00
/*
* Interface for OSS sequencer emulation
*
* Copyright ( C ) 1999 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
*
* Changes
* 19990227 Steve Ratcliffe Made separate file and merged in latest
* midi emulation .
*/
# ifdef CONFIG_SND_SEQUENCER_OSS
# include <asm/uaccess.h>
# include <sound/core.h>
# include "emux_voice.h"
# include <sound/asoundef.h>
2005-11-17 16:24:47 +03:00
static int snd_emux_open_seq_oss ( struct snd_seq_oss_arg * arg , void * closure ) ;
static int snd_emux_close_seq_oss ( struct snd_seq_oss_arg * arg ) ;
static int snd_emux_ioctl_seq_oss ( struct snd_seq_oss_arg * arg , unsigned int cmd ,
unsigned long ioarg ) ;
static int snd_emux_load_patch_seq_oss ( struct snd_seq_oss_arg * arg , int format ,
const char __user * buf , int offs , int count ) ;
static int snd_emux_reset_seq_oss ( struct snd_seq_oss_arg * arg ) ;
static int snd_emux_event_oss_input ( struct snd_seq_event * ev , int direct ,
void * private , int atomic , int hop ) ;
static void reset_port_mode ( struct snd_emux_port * port , int midi_mode ) ;
static void emuspec_control ( struct snd_emux * emu , struct snd_emux_port * port ,
int cmd , unsigned char * event , int atomic , int hop ) ;
static void gusspec_control ( struct snd_emux * emu , struct snd_emux_port * port ,
int cmd , unsigned char * event , int atomic , int hop ) ;
static void fake_event ( struct snd_emux * emu , struct snd_emux_port * port ,
int ch , int param , int val , int atomic , int hop ) ;
2005-04-17 02:20:36 +04:00
/* operators */
2005-11-17 16:24:47 +03:00
static struct snd_seq_oss_callback oss_callback = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. open = snd_emux_open_seq_oss ,
. close = snd_emux_close_seq_oss ,
. ioctl = snd_emux_ioctl_seq_oss ,
. load_patch = snd_emux_load_patch_seq_oss ,
. reset = snd_emux_reset_seq_oss ,
} ;
/*
* register OSS synth
*/
void
2005-11-17 16:24:47 +03:00
snd_emux_init_seq_oss ( struct snd_emux * emu )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:24:47 +03:00
struct snd_seq_oss_reg * arg ;
struct snd_seq_device * dev ;
2005-04-17 02:20:36 +04:00
if ( snd_seq_device_new ( emu - > card , 0 , SNDRV_SEQ_DEV_ID_OSS ,
2005-11-17 16:24:47 +03:00
sizeof ( struct snd_seq_oss_reg ) , & dev ) < 0 )
2005-04-17 02:20:36 +04:00
return ;
emu - > oss_synth = dev ;
strcpy ( dev - > name , emu - > name ) ;
arg = SNDRV_SEQ_DEVICE_ARGPTR ( dev ) ;
arg - > type = SYNTH_TYPE_SAMPLE ;
arg - > subtype = SAMPLE_TYPE_AWE32 ;
arg - > nvoices = emu - > max_voices ;
arg - > oper = oss_callback ;
arg - > private_data = emu ;
/* register to OSS synth table */
snd_device_register ( emu - > card , dev ) ;
}
/*
* unregister
*/
void
2005-11-17 16:24:47 +03:00
snd_emux_detach_seq_oss ( struct snd_emux * emu )
2005-04-17 02:20:36 +04:00
{
if ( emu - > oss_synth ) {
snd_device_free ( emu - > card , emu - > oss_synth ) ;
emu - > oss_synth = NULL ;
}
}
/* use port number as a unique soundfont client number */
# define SF_CLIENT_NO(p) ((p) + 0x1000)
/*
* open port for OSS sequencer
*/
static int
2005-11-17 16:24:47 +03:00
snd_emux_open_seq_oss ( struct snd_seq_oss_arg * arg , void * closure )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:24:47 +03:00
struct snd_emux * emu ;
struct snd_emux_port * p ;
struct snd_seq_port_callback callback ;
2005-04-17 02:20:36 +04:00
char tmpname [ 64 ] ;
emu = closure ;
2008-08-08 19:12:47 +04:00
if ( snd_BUG_ON ( ! arg | | ! emu ) )
return - ENXIO ;
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
if ( ! 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 - EFAULT ;
}
memset ( & callback , 0 , sizeof ( callback ) ) ;
callback . owner = THIS_MODULE ;
callback . event_input = snd_emux_event_oss_input ;
sprintf ( tmpname , " %s OSS Port " , emu - > name ) ;
p = snd_emux_create_port ( emu , tmpname , 32 ,
1 , & callback ) ;
if ( p = = NULL ) {
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
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 - ENOMEM ;
}
/* fill the argument data */
arg - > private_data = p ;
arg - > addr . client = p - > chset . client ;
arg - > addr . port = p - > chset . port ;
p - > oss_arg = arg ;
reset_port_mode ( p , arg - > seq_mode ) ;
snd_emux_reset_port ( p ) ;
2006-01-16 18:31:42 +03:00
mutex_unlock ( & emu - > register_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
# define DEFAULT_DRUM_FLAGS ((1<<9) | (1<<25))
/*
* reset port mode
*/
static void
2005-11-17 16:24:47 +03:00
reset_port_mode ( struct snd_emux_port * port , int midi_mode )
2005-04-17 02:20:36 +04:00
{
if ( midi_mode ) {
port - > port_mode = SNDRV_EMUX_PORT_MODE_OSS_MIDI ;
port - > drum_flags = DEFAULT_DRUM_FLAGS ;
port - > volume_atten = 0 ;
port - > oss_arg - > event_passing = SNDRV_SEQ_OSS_PROCESS_KEYPRESS ;
} else {
port - > port_mode = SNDRV_EMUX_PORT_MODE_OSS_SYNTH ;
port - > drum_flags = 0 ;
port - > volume_atten = 32 ;
port - > oss_arg - > event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS ;
}
}
/*
* close port
*/
static int
2005-11-17 16:24:47 +03:00
snd_emux_close_seq_oss ( struct snd_seq_oss_arg * arg )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:24:47 +03:00
struct snd_emux * emu ;
struct snd_emux_port * p ;
2005-04-17 02:20:36 +04:00
2008-08-08 19:12:47 +04:00
if ( snd_BUG_ON ( ! arg ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
p = arg - > private_data ;
2008-08-08 19:12:47 +04:00
if ( snd_BUG_ON ( ! p ) )
return - ENXIO ;
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 - ENXIO ;
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 ) ;
snd_soundfont_close_check ( emu - > sflist , SF_CLIENT_NO ( p - > chset . port ) ) ;
snd_seq_event_port_detach ( p - > chset . client , p - > chset . port ) ;
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 ;
}
/*
* load patch
*/
static int
2005-11-17 16:24:47 +03:00
snd_emux_load_patch_seq_oss ( struct snd_seq_oss_arg * arg , int format ,
2005-04-17 02:20:36 +04:00
const char __user * buf , int offs , int count )
{
2005-11-17 16:24:47 +03:00
struct snd_emux * emu ;
struct snd_emux_port * p ;
2005-04-17 02:20:36 +04:00
int rc ;
2008-08-08 19:12:47 +04:00
if ( snd_BUG_ON ( ! arg ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
p = arg - > private_data ;
2008-08-08 19:12:47 +04:00
if ( snd_BUG_ON ( ! p ) )
return - ENXIO ;
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 - ENXIO ;
2005-04-17 02:20:36 +04:00
if ( format = = GUS_PATCH )
rc = snd_soundfont_load_guspatch ( emu - > sflist , buf , count ,
SF_CLIENT_NO ( p - > chset . port ) ) ;
else if ( format = = SNDRV_OSS_SOUNDFONT_PATCH ) {
2005-11-17 16:24:47 +03:00
struct soundfont_patch_info patch ;
2005-04-17 02:20:36 +04:00
if ( count < ( int ) sizeof ( patch ) )
rc = - EINVAL ;
if ( copy_from_user ( & patch , buf , sizeof ( patch ) ) )
rc = - EFAULT ;
if ( patch . type > = SNDRV_SFNT_LOAD_INFO & &
patch . type < = SNDRV_SFNT_PROBE_DATA )
rc = snd_soundfont_load ( emu - > sflist , buf , count , SF_CLIENT_NO ( p - > chset . port ) ) ;
else {
if ( emu - > ops . load_fx )
rc = emu - > ops . load_fx ( emu , patch . type , patch . optarg , buf , count ) ;
else
rc = - EINVAL ;
}
} else
rc = 0 ;
return rc ;
}
/*
* ioctl
*/
static int
2005-11-17 16:24:47 +03:00
snd_emux_ioctl_seq_oss ( struct snd_seq_oss_arg * arg , unsigned int cmd , unsigned long ioarg )
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
2008-08-08 19:12:47 +04:00
if ( snd_BUG_ON ( ! arg ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
p = arg - > private_data ;
2008-08-08 19:12:47 +04:00
if ( snd_BUG_ON ( ! p ) )
return - ENXIO ;
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 - ENXIO ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case SNDCTL_SEQ_RESETSAMPLES :
snd_soundfont_remove_samples ( emu - > sflist ) ;
return 0 ;
case SNDCTL_SYNTH_MEMAVL :
if ( emu - > memhdr )
return snd_util_mem_avail ( emu - > memhdr ) ;
return 0 ;
}
return 0 ;
}
/*
* reset device
*/
static int
2005-11-17 16:24:47 +03:00
snd_emux_reset_seq_oss ( struct snd_seq_oss_arg * arg )
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
2008-08-08 19:12:47 +04:00
if ( snd_BUG_ON ( ! arg ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
p = arg - > private_data ;
2008-08-08 19:12:47 +04:00
if ( snd_BUG_ON ( ! p ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
snd_emux_reset_port ( p ) ;
return 0 ;
}
/*
* receive raw events : only SEQ_PRIVATE is accepted .
*/
static int
2005-11-17 16:24:47 +03:00
snd_emux_event_oss_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 * emu ;
struct snd_emux_port * p ;
2005-04-17 02:20:36 +04:00
unsigned char cmd , * data ;
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
if ( ev - > type ! = SNDRV_SEQ_EVENT_OSS )
return snd_emux_event_input ( ev , direct , private_data , atomic , hop ) ;
data = ev - > data . raw8 . d ;
/* only SEQ_PRIVATE is accepted */
if ( data [ 0 ] ! = 0xfe )
return 0 ;
cmd = data [ 2 ] & _EMUX_OSS_MODE_VALUE_MASK ;
if ( data [ 2 ] & _EMUX_OSS_MODE_FLAG )
emuspec_control ( emu , p , cmd , data , atomic , hop ) ;
else
gusspec_control ( emu , p , cmd , data , atomic , hop ) ;
return 0 ;
}
/*
* OSS / AWE driver specific h / w controls
*/
static void
2005-11-17 16:24:47 +03:00
emuspec_control ( struct snd_emux * emu , struct snd_emux_port * port , int cmd ,
2005-04-17 02:20:36 +04:00
unsigned char * event , int atomic , int hop )
{
int voice ;
unsigned short p1 ;
short p2 ;
int i ;
2005-11-17 16:24:47 +03:00
struct snd_midi_channel * chan ;
2005-04-17 02:20:36 +04:00
voice = event [ 3 ] ;
if ( voice < 0 | | voice > = port - > chset . max_channels )
chan = NULL ;
else
chan = & port - > chset . channels [ voice ] ;
p1 = * ( unsigned short * ) & event [ 4 ] ;
p2 = * ( short * ) & event [ 6 ] ;
switch ( cmd ) {
#if 0 /* don't do this atomically */
case _EMUX_OSS_REMOVE_LAST_SAMPLES :
snd_soundfont_remove_unlocked ( emu - > sflist ) ;
break ;
# endif
case _EMUX_OSS_SEND_EFFECT :
if ( chan )
snd_emux_send_effect_oss ( port , chan , p1 , p2 ) ;
break ;
case _EMUX_OSS_TERMINATE_ALL :
snd_emux_terminate_all ( emu ) ;
break ;
case _EMUX_OSS_TERMINATE_CHANNEL :
/*snd_emux_mute_channel(emu, chan);*/
break ;
case _EMUX_OSS_RESET_CHANNEL :
/*snd_emux_channel_init(chset, chan);*/
break ;
case _EMUX_OSS_RELEASE_ALL :
fake_event ( emu , port , voice , MIDI_CTL_ALL_NOTES_OFF , 0 , atomic , hop ) ;
break ;
case _EMUX_OSS_NOTEOFF_ALL :
fake_event ( emu , port , voice , MIDI_CTL_ALL_SOUNDS_OFF , 0 , atomic , hop ) ;
break ;
case _EMUX_OSS_INITIAL_VOLUME :
if ( p2 ) {
port - > volume_atten = ( short ) p1 ;
snd_emux_update_port ( port , SNDRV_EMUX_UPDATE_VOLUME ) ;
}
break ;
case _EMUX_OSS_CHN_PRESSURE :
if ( chan ) {
chan - > midi_pressure = p1 ;
snd_emux_update_channel ( port , chan , SNDRV_EMUX_UPDATE_FMMOD | SNDRV_EMUX_UPDATE_FM2FRQ2 ) ;
}
break ;
case _EMUX_OSS_CHANNEL_MODE :
reset_port_mode ( port , p1 ) ;
snd_emux_reset_port ( port ) ;
break ;
case _EMUX_OSS_DRUM_CHANNELS :
port - > drum_flags = * ( unsigned int * ) & event [ 4 ] ;
for ( i = 0 ; i < port - > chset . max_channels ; i + + ) {
chan = & port - > chset . channels [ i ] ;
chan - > drum_channel = ( ( port - > drum_flags > > i ) & 1 ) ? 1 : 0 ;
}
break ;
case _EMUX_OSS_MISC_MODE :
if ( p1 < EMUX_MD_END )
port - > ctrls [ p1 ] = p2 ;
break ;
case _EMUX_OSS_DEBUG_MODE :
break ;
default :
if ( emu - > ops . oss_ioctl )
emu - > ops . oss_ioctl ( emu , cmd , p1 , p2 ) ;
break ;
}
}
/*
* GUS specific h / w controls
*/
# include <linux/ultrasound.h>
static void
2005-11-17 16:24:47 +03:00
gusspec_control ( struct snd_emux * emu , struct snd_emux_port * port , int cmd ,
2005-04-17 02:20:36 +04:00
unsigned char * event , int atomic , int hop )
{
int voice ;
unsigned short p1 ;
short p2 ;
int plong ;
2005-11-17 16:24:47 +03:00
struct snd_midi_channel * chan ;
2005-04-17 02:20:36 +04:00
if ( port - > port_mode ! = SNDRV_EMUX_PORT_MODE_OSS_SYNTH )
return ;
if ( cmd = = _GUS_NUMVOICES )
return ;
voice = event [ 3 ] ;
if ( voice < 0 | | voice > = port - > chset . max_channels )
return ;
chan = & port - > chset . channels [ voice ] ;
p1 = * ( unsigned short * ) & event [ 4 ] ;
p2 = * ( short * ) & event [ 6 ] ;
plong = * ( int * ) & event [ 4 ] ;
switch ( cmd ) {
case _GUS_VOICESAMPLE :
chan - > midi_program = p1 ;
return ;
case _GUS_VOICEBALA :
/* 0 to 15 --> 0 to 127 */
chan - > control [ MIDI_CTL_MSB_PAN ] = ( int ) p1 < < 3 ;
snd_emux_update_channel ( port , chan , SNDRV_EMUX_UPDATE_PAN ) ;
return ;
case _GUS_VOICEVOL :
case _GUS_VOICEVOL2 :
/* not supported yet */
return ;
case _GUS_RAMPRANGE :
case _GUS_RAMPRATE :
case _GUS_RAMPMODE :
case _GUS_RAMPON :
case _GUS_RAMPOFF :
/* volume ramping not supported */
return ;
case _GUS_VOLUME_SCALE :
return ;
case _GUS_VOICE_POS :
# ifdef SNDRV_EMUX_USE_RAW_EFFECT
snd_emux_send_effect ( port , chan , EMUX_FX_SAMPLE_START ,
( short ) ( plong & 0x7fff ) ,
EMUX_FX_FLAG_SET ) ;
snd_emux_send_effect ( port , chan , EMUX_FX_COARSE_SAMPLE_START ,
( plong > > 15 ) & 0xffff ,
EMUX_FX_FLAG_SET ) ;
# endif
return ;
}
}
/*
* send an event to midi emulation
*/
static void
2005-11-17 16:24:47 +03:00
fake_event ( struct snd_emux * emu , struct snd_emux_port * port , int ch , int param , int val , int atomic , int hop )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:24:47 +03:00
struct snd_seq_event ev ;
2005-04-17 02:20:36 +04:00
memset ( & ev , 0 , sizeof ( ev ) ) ;
ev . type = SNDRV_SEQ_EVENT_CONTROLLER ;
ev . data . control . channel = ch ;
ev . data . control . param = param ;
ev . data . control . value = val ;
snd_emux_event_input ( & ev , 0 , port , atomic , hop ) ;
}
# endif /* CONFIG_SND_SEQUENCER_OSS */