2005-04-16 15:20:36 -07:00
/*
* Interface for OSS sequencer emulation
*
* Copyright ( C ) 2000 Uros Bizjak < uros @ kss - loka . si >
*
* 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
*/
2011-09-22 09:34:58 -04:00
# include <linux/export.h>
2005-04-16 15:20:36 -07:00
# include "opl3_voice.h"
2005-11-17 14:13:14 +01:00
static int snd_opl3_open_seq_oss ( struct snd_seq_oss_arg * arg , void * closure ) ;
static int snd_opl3_close_seq_oss ( struct snd_seq_oss_arg * arg ) ;
static int snd_opl3_ioctl_seq_oss ( struct snd_seq_oss_arg * arg , unsigned int cmd , unsigned long ioarg ) ;
static int snd_opl3_load_patch_seq_oss ( struct snd_seq_oss_arg * arg , int format , const char __user * buf , int offs , int count ) ;
static int snd_opl3_reset_seq_oss ( struct snd_seq_oss_arg * arg ) ;
2005-04-16 15:20:36 -07:00
/* */
static inline mm_segment_t snd_enter_user ( void )
{
mm_segment_t fs = get_fs ( ) ;
set_fs ( get_ds ( ) ) ;
return fs ;
}
static inline void snd_leave_user ( mm_segment_t fs )
{
set_fs ( fs ) ;
}
/* operators */
2005-11-17 14:13:14 +01:00
extern struct snd_midi_op opl3_ops ;
2005-04-16 15:20:36 -07:00
2005-11-17 14:13:14 +01:00
static struct snd_seq_oss_callback oss_callback = {
2005-04-16 15:20:36 -07:00
. owner = THIS_MODULE ,
. open = snd_opl3_open_seq_oss ,
. close = snd_opl3_close_seq_oss ,
. ioctl = snd_opl3_ioctl_seq_oss ,
. load_patch = snd_opl3_load_patch_seq_oss ,
. reset = snd_opl3_reset_seq_oss ,
} ;
2005-11-17 14:13:14 +01:00
static int snd_opl3_oss_event_input ( struct snd_seq_event * ev , int direct ,
2005-04-16 15:20:36 -07:00
void * private_data , int atomic , int hop )
{
2005-11-17 14:13:14 +01:00
struct snd_opl3 * opl3 = private_data ;
2005-04-16 15:20:36 -07:00
if ( ev - > type ! = SNDRV_SEQ_EVENT_OSS )
snd_midi_process_event ( & opl3_ops , ev , opl3 - > oss_chset ) ;
return 0 ;
}
/* ------------------------------ */
static void snd_opl3_oss_free_port ( void * private_data )
{
2005-11-17 14:13:14 +01:00
struct snd_opl3 * opl3 = private_data ;
2005-04-16 15:20:36 -07:00
snd_midi_channel_free_set ( opl3 - > oss_chset ) ;
}
2005-11-17 14:13:14 +01:00
static int snd_opl3_oss_create_port ( struct snd_opl3 * opl3 )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:13:14 +01:00
struct snd_seq_port_callback callbacks ;
2005-04-16 15:20:36 -07:00
char name [ 32 ] ;
int voices , opl_ver ;
voices = ( opl3 - > hardware < OPL3_HW_OPL3 ) ?
MAX_OPL2_VOICES : MAX_OPL3_VOICES ;
opl3 - > oss_chset = snd_midi_channel_alloc_set ( voices ) ;
if ( opl3 - > oss_chset = = NULL )
return - ENOMEM ;
opl3 - > oss_chset - > private_data = opl3 ;
memset ( & callbacks , 0 , sizeof ( callbacks ) ) ;
callbacks . owner = THIS_MODULE ;
callbacks . event_input = snd_opl3_oss_event_input ;
callbacks . private_free = snd_opl3_oss_free_port ;
callbacks . private_data = opl3 ;
opl_ver = ( opl3 - > hardware & OPL3_HW_MASK ) > > 8 ;
sprintf ( name , " OPL%i OSS Port " , opl_ver ) ;
opl3 - > oss_chset - > client = opl3 - > seq_client ;
opl3 - > oss_chset - > port = snd_seq_event_port_attach ( opl3 - > seq_client , & callbacks ,
SNDRV_SEQ_PORT_CAP_WRITE ,
SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
2006-05-02 16:08:41 +02:00
SNDRV_SEQ_PORT_TYPE_MIDI_GM |
SNDRV_SEQ_PORT_TYPE_HARDWARE |
SNDRV_SEQ_PORT_TYPE_SYNTHESIZER ,
2005-04-16 15:20:36 -07:00
voices , voices ,
name ) ;
if ( opl3 - > oss_chset - > port < 0 ) {
2006-03-06 14:03:37 +01:00
int port ;
port = opl3 - > oss_chset - > port ;
2005-04-16 15:20:36 -07:00
snd_midi_channel_free_set ( opl3 - > oss_chset ) ;
2006-03-06 14:03:37 +01:00
return port ;
2005-04-16 15:20:36 -07:00
}
return 0 ;
}
/* ------------------------------ */
/* register OSS synth */
2005-11-17 14:13:14 +01:00
void snd_opl3_init_seq_oss ( struct snd_opl3 * opl3 , char * name )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:13:14 +01:00
struct snd_seq_oss_reg * arg ;
struct snd_seq_device * dev ;
2005-04-16 15:20:36 -07:00
if ( snd_seq_device_new ( opl3 - > card , 0 , SNDRV_SEQ_DEV_ID_OSS ,
2005-11-17 14:13:14 +01:00
sizeof ( struct snd_seq_oss_reg ) , & dev ) < 0 )
2005-04-16 15:20:36 -07:00
return ;
opl3 - > oss_seq_dev = dev ;
strlcpy ( dev - > name , name , sizeof ( dev - > name ) ) ;
arg = SNDRV_SEQ_DEVICE_ARGPTR ( dev ) ;
arg - > type = SYNTH_TYPE_FM ;
if ( opl3 - > hardware < OPL3_HW_OPL3 ) {
arg - > subtype = FM_TYPE_ADLIB ;
arg - > nvoices = MAX_OPL2_VOICES ;
} else {
arg - > subtype = FM_TYPE_OPL3 ;
arg - > nvoices = MAX_OPL3_VOICES ;
}
arg - > oper = oss_callback ;
arg - > private_data = opl3 ;
2006-03-06 14:03:37 +01:00
if ( snd_opl3_oss_create_port ( opl3 ) ) {
/* register to OSS synth table */
snd_device_register ( opl3 - > card , dev ) ;
}
2005-04-16 15:20:36 -07:00
}
/* unregister */
2005-11-17 14:13:14 +01:00
void snd_opl3_free_seq_oss ( struct snd_opl3 * opl3 )
2005-04-16 15:20:36 -07:00
{
if ( opl3 - > oss_seq_dev ) {
2006-02-24 13:03:52 -08:00
/* The instance should have been released in prior */
2005-04-16 15:20:36 -07:00
opl3 - > oss_seq_dev = NULL ;
}
}
/* ------------------------------ */
/* open OSS sequencer */
2005-11-17 14:13:14 +01:00
static int snd_opl3_open_seq_oss ( struct snd_seq_oss_arg * arg , void * closure )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:13:14 +01:00
struct snd_opl3 * opl3 = closure ;
2005-04-16 15:20:36 -07:00
int err ;
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! arg ) )
return - ENXIO ;
2005-04-16 15:20:36 -07:00
if ( ( err = snd_opl3_synth_setup ( opl3 ) ) < 0 )
return err ;
/* fill the argument data */
arg - > private_data = opl3 ;
arg - > addr . client = opl3 - > oss_chset - > client ;
arg - > addr . port = opl3 - > oss_chset - > port ;
if ( ( err = snd_opl3_synth_use_inc ( opl3 ) ) < 0 )
return err ;
opl3 - > synth_mode = SNDRV_OPL3_MODE_SYNTH ;
return 0 ;
}
/* close OSS sequencer */
2005-11-17 14:13:14 +01:00
static int snd_opl3_close_seq_oss ( struct snd_seq_oss_arg * arg )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:13:14 +01:00
struct snd_opl3 * opl3 ;
2005-04-16 15:20:36 -07:00
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! arg ) )
return - ENXIO ;
2005-04-16 15:20:36 -07:00
opl3 = arg - > private_data ;
snd_opl3_synth_cleanup ( opl3 ) ;
snd_opl3_synth_use_dec ( opl3 ) ;
return 0 ;
}
/* load patch */
/* from sound_config.h */
# define SBFM_MAXINSTR 256
2005-11-17 14:13:14 +01:00
static int snd_opl3_load_patch_seq_oss ( struct snd_seq_oss_arg * arg , int format ,
2005-04-16 15:20:36 -07:00
const char __user * buf , int offs , int count )
{
2005-11-17 14:13:14 +01:00
struct snd_opl3 * opl3 ;
2007-10-30 11:49:22 +01:00
struct sbi_instrument sbi ;
char name [ 32 ] ;
int err , type ;
2005-04-16 15:20:36 -07:00
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! arg ) )
return - ENXIO ;
2005-04-16 15:20:36 -07:00
opl3 = arg - > private_data ;
2007-10-30 11:49:22 +01:00
if ( format = = FM_PATCH )
type = FM_PATCH_OPL2 ;
else if ( format = = OPL3_PATCH )
type = FM_PATCH_OPL3 ;
else
return - EINVAL ;
2005-04-16 15:20:36 -07:00
2007-10-30 11:49:22 +01:00
if ( count < ( int ) sizeof ( sbi ) ) {
2009-02-05 15:51:50 +01:00
snd_printk ( KERN_ERR " FM Error: Patch record too short \n " ) ;
2007-10-30 11:49:22 +01:00
return - EINVAL ;
}
if ( copy_from_user ( & sbi , buf , sizeof ( sbi ) ) )
return - EFAULT ;
2005-04-16 15:20:36 -07:00
2007-10-30 11:49:22 +01:00
if ( sbi . channel < 0 | | sbi . channel > = SBFM_MAXINSTR ) {
2009-02-05 15:51:50 +01:00
snd_printk ( KERN_ERR " FM Error: Invalid instrument number %d \n " ,
2007-10-30 11:49:22 +01:00
sbi . channel ) ;
return - EINVAL ;
}
2005-04-16 15:20:36 -07:00
2007-10-30 11:49:22 +01:00
memset ( name , 0 , sizeof ( name ) ) ;
sprintf ( name , " Chan%d " , sbi . channel ) ;
2005-04-16 15:20:36 -07:00
2007-10-30 11:49:22 +01:00
err = snd_opl3_load_patch ( opl3 , sbi . channel , 127 , type , name , NULL ,
sbi . operators ) ;
if ( err < 0 )
return err ;
2005-04-16 15:20:36 -07:00
2007-10-30 11:49:22 +01:00
return sizeof ( sbi ) ;
2005-04-16 15:20:36 -07:00
}
/* ioctl */
2005-11-17 14:13:14 +01:00
static int snd_opl3_ioctl_seq_oss ( struct snd_seq_oss_arg * arg , unsigned int cmd ,
2005-04-16 15:20:36 -07:00
unsigned long ioarg )
{
2005-11-17 14:13:14 +01:00
struct snd_opl3 * opl3 ;
2005-04-16 15:20:36 -07:00
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! arg ) )
return - ENXIO ;
2005-04-16 15:20:36 -07:00
opl3 = arg - > private_data ;
switch ( cmd ) {
case SNDCTL_FM_LOAD_INSTR :
2009-02-05 15:51:50 +01:00
snd_printk ( KERN_ERR " OPL3: "
" Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. "
" Fix the program. \n " ) ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
case SNDCTL_SYNTH_MEMAVL :
return 0x7fffffff ;
case SNDCTL_FM_4OP_ENABLE :
// handled automatically by OPL instrument type
return 0 ;
default :
return - EINVAL ;
}
return 0 ;
}
/* reset device */
2005-11-17 14:13:14 +01:00
static int snd_opl3_reset_seq_oss ( struct snd_seq_oss_arg * arg )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:13:14 +01:00
struct snd_opl3 * opl3 ;
2005-04-16 15:20:36 -07:00
2008-08-08 17:12:47 +02:00
if ( snd_BUG_ON ( ! arg ) )
return - ENXIO ;
2005-04-16 15:20:36 -07:00
opl3 = arg - > private_data ;
return 0 ;
}