2005-04-16 15:20:36 -07:00
/*
* Dummy soundcard
2007-10-15 09:50:19 +02:00
* Copyright ( c ) by Jaroslav Kysela < perex @ perex . cz >
2005-04-16 15:20:36 -07:00
*
* 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 <linux/init.h>
2005-11-17 16:01:56 +01:00
# include <linux/err.h>
# include <linux/platform_device.h>
2005-04-16 15:20:36 -07:00
# include <linux/jiffies.h>
# include <linux/slab.h>
# include <linux/time.h>
# include <linux/wait.h>
2009-09-03 15:59:26 +02:00
# include <linux/hrtimer.h>
# include <linux/math64.h>
2005-04-16 15:20:36 -07:00
# include <linux/moduleparam.h>
# include <sound/core.h>
# include <sound/control.h>
2006-08-23 13:07:19 +02:00
# include <sound/tlv.h>
2005-04-16 15:20:36 -07:00
# include <sound/pcm.h>
# include <sound/rawmidi.h>
2009-09-08 14:30:49 +02:00
# include <sound/info.h>
2005-04-16 15:20:36 -07:00
# include <sound/initval.h>
2007-10-15 09:50:19 +02:00
MODULE_AUTHOR ( " Jaroslav Kysela <perex@perex.cz> " ) ;
2005-04-16 15:20:36 -07:00
MODULE_DESCRIPTION ( " Dummy soundcard (/dev/null) " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_SUPPORTED_DEVICE ( " {{ALSA,Dummy soundcard}} " ) ;
# define MAX_PCM_DEVICES 4
2009-09-08 18:15:17 +02:00
# define MAX_PCM_SUBSTREAMS 128
2005-04-16 15:20:36 -07:00
# define MAX_MIDI_DEVICES 2
#if 0 /* emu10k1 emulation */
# define MAX_BUFFER_SIZE (128 * 1024)
2005-11-17 14:27:28 +01:00
static int emu10k1_playback_constraints ( struct snd_pcm_runtime * runtime )
2005-04-16 15:20:36 -07:00
{
int err ;
2008-08-15 12:59:02 +02:00
err = snd_pcm_hw_constraint_integer ( runtime , SNDRV_PCM_HW_PARAM_PERIODS ) ;
if ( err < 0 )
2005-04-16 15:20:36 -07:00
return err ;
2008-08-15 12:59:02 +02:00
err = snd_pcm_hw_constraint_minmax ( runtime , SNDRV_PCM_HW_PARAM_BUFFER_BYTES , 256 , UINT_MAX ) ;
2008-10-19 10:34:22 +02:00
if ( err < 0 )
2005-04-16 15:20:36 -07:00
return err ;
return 0 ;
}
# define add_playback_constraints emu10k1_playback_constraints
# endif
#if 0 /* RME9652 emulation */
# define MAX_BUFFER_SIZE (26 * 64 * 1024)
# define USE_FORMATS SNDRV_PCM_FMTBIT_S32_LE
# define USE_CHANNELS_MIN 26
# define USE_CHANNELS_MAX 26
# define USE_PERIODS_MIN 2
# define USE_PERIODS_MAX 2
# endif
#if 0 /* ICE1712 emulation */
# define MAX_BUFFER_SIZE (256 * 1024)
# define USE_FORMATS SNDRV_PCM_FMTBIT_S32_LE
# define USE_CHANNELS_MIN 10
# define USE_CHANNELS_MAX 10
# define USE_PERIODS_MIN 1
# define USE_PERIODS_MAX 1024
# endif
#if 0 /* UDA1341 emulation */
# define MAX_BUFFER_SIZE (16380)
# define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE
# define USE_CHANNELS_MIN 2
# define USE_CHANNELS_MAX 2
# define USE_PERIODS_MIN 2
# define USE_PERIODS_MAX 255
# endif
#if 0 /* simple AC97 bridge (intel8x0) with 48kHz AC97 only codec */
# define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE
# define USE_CHANNELS_MIN 2
# define USE_CHANNELS_MAX 2
# define USE_RATE SNDRV_PCM_RATE_48000
# define USE_RATE_MIN 48000
# define USE_RATE_MAX 48000
# endif
2006-01-03 14:27:21 +01:00
#if 0 /* CA0106 */
# define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE
# define USE_CHANNELS_MIN 2
# define USE_CHANNELS_MAX 2
# define USE_RATE (SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_96000|SNDRV_PCM_RATE_192000)
# define USE_RATE_MIN 48000
# define USE_RATE_MAX 192000
# define MAX_BUFFER_SIZE ((65536-64)*8)
# define MAX_PERIOD_SIZE (65536-64)
# define USE_PERIODS_MIN 2
# define USE_PERIODS_MAX 8
# endif
2005-04-16 15:20:36 -07:00
/* defaults */
# ifndef MAX_BUFFER_SIZE
# define MAX_BUFFER_SIZE (64*1024)
# endif
2006-01-03 14:27:21 +01:00
# ifndef MAX_PERIOD_SIZE
# define MAX_PERIOD_SIZE MAX_BUFFER_SIZE
# endif
2005-04-16 15:20:36 -07:00
# ifndef USE_FORMATS
# define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
# endif
# ifndef USE_RATE
# define USE_RATE SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000
# define USE_RATE_MIN 5500
# define USE_RATE_MAX 48000
# endif
# ifndef USE_CHANNELS_MIN
# define USE_CHANNELS_MIN 1
# endif
# ifndef USE_CHANNELS_MAX
# define USE_CHANNELS_MAX 2
# endif
# ifndef USE_PERIODS_MIN
# define USE_PERIODS_MIN 1
# endif
# ifndef USE_PERIODS_MAX
# define USE_PERIODS_MAX 1024
# endif
# ifndef add_playback_constraints
# define add_playback_constraints(x) 0
# endif
# ifndef add_capture_constraints
# define add_capture_constraints(x) 0
# endif
static int index [ SNDRV_CARDS ] = SNDRV_DEFAULT_IDX ; /* Index 0-MAX */
static char * id [ SNDRV_CARDS ] = SNDRV_DEFAULT_STR ; /* ID for this card */
static int enable [ SNDRV_CARDS ] = { 1 , [ 1 . . . ( SNDRV_CARDS - 1 ) ] = 0 } ;
static int pcm_devs [ SNDRV_CARDS ] = { [ 0 . . . ( SNDRV_CARDS - 1 ) ] = 1 } ;
static int pcm_substreams [ SNDRV_CARDS ] = { [ 0 . . . ( SNDRV_CARDS - 1 ) ] = 8 } ;
//static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
2009-09-03 15:59:26 +02:00
# ifdef CONFIG_HIGH_RES_TIMERS
static int hrtimer = 1 ;
# endif
2009-09-04 12:19:36 +02:00
static int fake_buffer = 1 ;
2005-04-16 15:20:36 -07:00
module_param_array ( index , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( index , " Index value for dummy soundcard. " ) ;
module_param_array ( id , charp , NULL , 0444 ) ;
MODULE_PARM_DESC ( id , " ID string for dummy soundcard. " ) ;
module_param_array ( enable , bool , NULL , 0444 ) ;
MODULE_PARM_DESC ( enable , " Enable this dummy soundcard. " ) ;
module_param_array ( pcm_devs , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( pcm_devs , " PCM devices # (0-4) for dummy driver. " ) ;
module_param_array ( pcm_substreams , int , NULL , 0444 ) ;
2009-11-02 14:10:59 +01:00
MODULE_PARM_DESC ( pcm_substreams , " PCM substreams # (1-128) for dummy driver. " ) ;
2005-04-16 15:20:36 -07:00
//module_param_array(midi_devs, int, NULL, 0444);
//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver.");
2009-09-04 12:19:36 +02:00
module_param ( fake_buffer , bool , 0444 ) ;
MODULE_PARM_DESC ( fake_buffer , " Fake buffer allocations. " ) ;
2009-09-03 15:59:26 +02:00
# ifdef CONFIG_HIGH_RES_TIMERS
module_param ( hrtimer , bool , 0644 ) ;
MODULE_PARM_DESC ( hrtimer , " Use hrtimer as the timer source. " ) ;
# endif
2005-04-16 15:20:36 -07:00
2005-12-07 09:13:42 +01:00
static struct platform_device * devices [ SNDRV_CARDS ] ;
2005-04-16 15:20:36 -07:00
# define MIXER_ADDR_MASTER 0
# define MIXER_ADDR_LINE 1
# define MIXER_ADDR_MIC 2
# define MIXER_ADDR_SYNTH 3
# define MIXER_ADDR_CD 4
# define MIXER_ADDR_LAST 4
2009-09-03 15:59:26 +02:00
struct dummy_timer_ops {
int ( * create ) ( struct snd_pcm_substream * ) ;
void ( * free ) ( struct snd_pcm_substream * ) ;
int ( * prepare ) ( struct snd_pcm_substream * ) ;
int ( * start ) ( struct snd_pcm_substream * ) ;
int ( * stop ) ( struct snd_pcm_substream * ) ;
snd_pcm_uframes_t ( * pointer ) ( struct snd_pcm_substream * ) ;
} ;
2005-11-17 14:27:28 +01:00
struct snd_dummy {
struct snd_card * card ;
2005-11-17 16:01:56 +01:00
struct snd_pcm * pcm ;
2005-04-16 15:20:36 -07:00
spinlock_t mixer_lock ;
int mixer_volume [ MIXER_ADDR_LAST + 1 ] [ 2 ] ;
int capture_source [ MIXER_ADDR_LAST + 1 ] [ 2 ] ;
2009-09-03 15:59:26 +02:00
const struct dummy_timer_ops * timer_ops ;
2005-11-17 14:27:28 +01:00
} ;
2005-04-16 15:20:36 -07:00
2009-09-03 15:59:26 +02:00
/*
* system timer interface
*/
struct dummy_systimer_pcm {
2005-04-16 15:20:36 -07:00
spinlock_t lock ;
struct timer_list timer ;
2009-09-03 16:01:06 +02:00
unsigned long base_time ;
unsigned int frac_pos ; /* fractional sample position (based HZ) */
2009-09-04 08:45:11 +02:00
unsigned int frac_period_rest ;
2009-09-03 16:01:06 +02:00
unsigned int frac_buffer_size ; /* buffer_size * HZ */
unsigned int frac_period_size ; /* period_size * HZ */
unsigned int rate ;
2009-09-04 08:45:11 +02:00
int elapsed ;
2005-11-17 14:27:28 +01:00
struct snd_pcm_substream * substream ;
} ;
2005-04-16 15:20:36 -07:00
2009-09-03 16:01:06 +02:00
static void dummy_systimer_rearm ( struct dummy_systimer_pcm * dpcm )
{
dpcm - > timer . expires = jiffies +
2009-09-04 08:45:11 +02:00
( dpcm - > frac_period_rest + dpcm - > rate - 1 ) / dpcm - > rate ;
2009-09-03 16:01:06 +02:00
add_timer ( & dpcm - > timer ) ;
}
static void dummy_systimer_update ( struct dummy_systimer_pcm * dpcm )
{
unsigned long delta ;
delta = jiffies - dpcm - > base_time ;
if ( ! delta )
return ;
2009-09-04 08:45:11 +02:00
dpcm - > base_time + = delta ;
delta * = dpcm - > rate ;
dpcm - > frac_pos + = delta ;
2009-09-03 16:01:06 +02:00
while ( dpcm - > frac_pos > = dpcm - > frac_buffer_size )
dpcm - > frac_pos - = dpcm - > frac_buffer_size ;
2009-09-04 08:45:11 +02:00
while ( dpcm - > frac_period_rest < = delta ) {
dpcm - > elapsed + + ;
dpcm - > frac_period_rest + = dpcm - > frac_period_size ;
}
dpcm - > frac_period_rest - = delta ;
2009-09-03 16:01:06 +02:00
}
2009-09-03 15:59:26 +02:00
static int dummy_systimer_start ( struct snd_pcm_substream * substream )
2005-04-16 15:20:36 -07:00
{
2009-09-03 15:59:26 +02:00
struct dummy_systimer_pcm * dpcm = substream - > runtime - > private_data ;
spin_lock ( & dpcm - > lock ) ;
2009-09-03 16:01:06 +02:00
dpcm - > base_time = jiffies ;
dummy_systimer_rearm ( dpcm ) ;
2009-09-03 15:59:26 +02:00
spin_unlock ( & dpcm - > lock ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2009-09-03 15:59:26 +02:00
static int dummy_systimer_stop ( struct snd_pcm_substream * substream )
2005-04-16 15:20:36 -07:00
{
2009-09-03 15:59:26 +02:00
struct dummy_systimer_pcm * dpcm = substream - > runtime - > private_data ;
2005-11-17 10:20:23 +01:00
spin_lock ( & dpcm - > lock ) ;
2009-09-03 15:59:26 +02:00
del_timer ( & dpcm - > timer ) ;
2005-11-17 10:20:23 +01:00
spin_unlock ( & dpcm - > lock ) ;
2005-11-17 16:01:56 +01:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2009-09-03 15:59:26 +02:00
static int dummy_systimer_prepare ( struct snd_pcm_substream * substream )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:27:28 +01:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2009-09-03 15:59:26 +02:00
struct dummy_systimer_pcm * dpcm = runtime - > private_data ;
2008-04-16 19:30:30 +02:00
2009-09-03 16:01:06 +02:00
dpcm - > frac_pos = 0 ;
dpcm - > rate = runtime - > rate ;
dpcm - > frac_buffer_size = runtime - > buffer_size * HZ ;
dpcm - > frac_period_size = runtime - > period_size * HZ ;
2009-09-04 08:45:11 +02:00
dpcm - > frac_period_rest = dpcm - > frac_period_size ;
dpcm - > elapsed = 0 ;
2008-02-21 07:55:30 +01:00
2005-04-16 15:20:36 -07:00
return 0 ;
}
2009-09-03 15:59:26 +02:00
static void dummy_systimer_callback ( unsigned long data )
2005-04-16 15:20:36 -07:00
{
2009-09-03 15:59:26 +02:00
struct dummy_systimer_pcm * dpcm = ( struct dummy_systimer_pcm * ) data ;
2005-11-18 18:52:14 +01:00
unsigned long flags ;
2009-09-04 08:45:11 +02:00
int elapsed = 0 ;
2005-04-16 15:20:36 -07:00
2005-11-18 18:52:14 +01:00
spin_lock_irqsave ( & dpcm - > lock , flags ) ;
2009-09-03 16:01:06 +02:00
dummy_systimer_update ( dpcm ) ;
dummy_systimer_rearm ( dpcm ) ;
2009-09-04 08:45:11 +02:00
elapsed = dpcm - > elapsed ;
dpcm - > elapsed = 0 ;
2009-09-03 16:01:06 +02:00
spin_unlock_irqrestore ( & dpcm - > lock , flags ) ;
2009-09-04 08:45:11 +02:00
if ( elapsed )
snd_pcm_period_elapsed ( dpcm - > substream ) ;
2005-04-16 15:20:36 -07:00
}
2009-09-03 15:59:26 +02:00
static snd_pcm_uframes_t
dummy_systimer_pointer ( struct snd_pcm_substream * substream )
2005-04-16 15:20:36 -07:00
{
2009-09-03 16:01:06 +02:00
struct dummy_systimer_pcm * dpcm = substream - > runtime - > private_data ;
2009-09-04 08:45:11 +02:00
snd_pcm_uframes_t pos ;
2005-04-16 15:20:36 -07:00
2009-09-03 16:01:06 +02:00
spin_lock ( & dpcm - > lock ) ;
dummy_systimer_update ( dpcm ) ;
2009-09-04 08:45:11 +02:00
pos = dpcm - > frac_pos / HZ ;
2009-09-03 16:01:06 +02:00
spin_unlock ( & dpcm - > lock ) ;
2009-09-04 08:45:11 +02:00
return pos ;
2005-04-16 15:20:36 -07:00
}
2009-09-03 15:59:26 +02:00
static int dummy_systimer_create ( struct snd_pcm_substream * substream )
2005-04-16 15:20:36 -07:00
{
2009-09-03 15:59:26 +02:00
struct dummy_systimer_pcm * dpcm ;
dpcm = kzalloc ( sizeof ( * dpcm ) , GFP_KERNEL ) ;
if ( ! dpcm )
return - ENOMEM ;
substream - > runtime - > private_data = dpcm ;
init_timer ( & dpcm - > timer ) ;
dpcm - > timer . data = ( unsigned long ) dpcm ;
dpcm - > timer . function = dummy_systimer_callback ;
spin_lock_init ( & dpcm - > lock ) ;
dpcm - > substream = substream ;
return 0 ;
}
static void dummy_systimer_free ( struct snd_pcm_substream * substream )
{
kfree ( substream - > runtime - > private_data ) ;
}
static struct dummy_timer_ops dummy_systimer_ops = {
. create = dummy_systimer_create ,
. free = dummy_systimer_free ,
. prepare = dummy_systimer_prepare ,
. start = dummy_systimer_start ,
. stop = dummy_systimer_stop ,
. pointer = dummy_systimer_pointer ,
2005-04-16 15:20:36 -07:00
} ;
2009-09-03 15:59:26 +02:00
# ifdef CONFIG_HIGH_RES_TIMERS
/*
* hrtimer interface
*/
struct dummy_hrtimer_pcm {
ktime_t base_time ;
ktime_t period_time ;
atomic_t running ;
struct hrtimer timer ;
struct tasklet_struct tasklet ;
struct snd_pcm_substream * substream ;
} ;
static void dummy_hrtimer_pcm_elapsed ( unsigned long priv )
{
struct dummy_hrtimer_pcm * dpcm = ( struct dummy_hrtimer_pcm * ) priv ;
if ( atomic_read ( & dpcm - > running ) )
snd_pcm_period_elapsed ( dpcm - > substream ) ;
}
static enum hrtimer_restart dummy_hrtimer_callback ( struct hrtimer * timer )
{
struct dummy_hrtimer_pcm * dpcm ;
dpcm = container_of ( timer , struct dummy_hrtimer_pcm , timer ) ;
if ( ! atomic_read ( & dpcm - > running ) )
return HRTIMER_NORESTART ;
tasklet_schedule ( & dpcm - > tasklet ) ;
hrtimer_forward_now ( timer , dpcm - > period_time ) ;
return HRTIMER_RESTART ;
}
static int dummy_hrtimer_start ( struct snd_pcm_substream * substream )
{
struct dummy_hrtimer_pcm * dpcm = substream - > runtime - > private_data ;
dpcm - > base_time = hrtimer_cb_get_time ( & dpcm - > timer ) ;
hrtimer_start ( & dpcm - > timer , dpcm - > period_time , HRTIMER_MODE_REL ) ;
atomic_set ( & dpcm - > running , 1 ) ;
return 0 ;
}
static int dummy_hrtimer_stop ( struct snd_pcm_substream * substream )
{
struct dummy_hrtimer_pcm * dpcm = substream - > runtime - > private_data ;
atomic_set ( & dpcm - > running , 0 ) ;
hrtimer_cancel ( & dpcm - > timer ) ;
return 0 ;
}
static inline void dummy_hrtimer_sync ( struct dummy_hrtimer_pcm * dpcm )
{
tasklet_kill ( & dpcm - > tasklet ) ;
}
static snd_pcm_uframes_t
dummy_hrtimer_pointer ( struct snd_pcm_substream * substream )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct dummy_hrtimer_pcm * dpcm = runtime - > private_data ;
u64 delta ;
u32 pos ;
delta = ktime_us_delta ( hrtimer_cb_get_time ( & dpcm - > timer ) ,
dpcm - > base_time ) ;
delta = div_u64 ( delta * runtime - > rate + 999999 , 1000000 ) ;
div_u64_rem ( delta , runtime - > buffer_size , & pos ) ;
return pos ;
}
static int dummy_hrtimer_prepare ( struct snd_pcm_substream * substream )
2005-04-16 15:20:36 -07:00
{
2009-09-03 15:59:26 +02:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct dummy_hrtimer_pcm * dpcm = runtime - > private_data ;
unsigned int period , rate ;
long sec ;
unsigned long nsecs ;
dummy_hrtimer_sync ( dpcm ) ;
period = runtime - > period_size ;
rate = runtime - > rate ;
sec = period / rate ;
period % = rate ;
nsecs = div_u64 ( ( u64 ) period * 1000000000UL + rate - 1 , rate ) ;
dpcm - > period_time = ktime_set ( sec , nsecs ) ;
return 0 ;
}
static int dummy_hrtimer_create ( struct snd_pcm_substream * substream )
{
struct dummy_hrtimer_pcm * dpcm ;
dpcm = kzalloc ( sizeof ( * dpcm ) , GFP_KERNEL ) ;
if ( ! dpcm )
return - ENOMEM ;
substream - > runtime - > private_data = dpcm ;
hrtimer_init ( & dpcm - > timer , CLOCK_MONOTONIC , HRTIMER_MODE_REL ) ;
dpcm - > timer . function = dummy_hrtimer_callback ;
dpcm - > substream = substream ;
atomic_set ( & dpcm - > running , 0 ) ;
tasklet_init ( & dpcm - > tasklet , dummy_hrtimer_pcm_elapsed ,
( unsigned long ) dpcm ) ;
return 0 ;
}
static void dummy_hrtimer_free ( struct snd_pcm_substream * substream )
{
struct dummy_hrtimer_pcm * dpcm = substream - > runtime - > private_data ;
dummy_hrtimer_sync ( dpcm ) ;
kfree ( dpcm ) ;
}
static struct dummy_timer_ops dummy_hrtimer_ops = {
. create = dummy_hrtimer_create ,
. free = dummy_hrtimer_free ,
. prepare = dummy_hrtimer_prepare ,
. start = dummy_hrtimer_start ,
. stop = dummy_hrtimer_stop ,
. pointer = dummy_hrtimer_pointer ,
} ;
# endif /* CONFIG_HIGH_RES_TIMERS */
/*
* PCM interface
*/
static int dummy_pcm_trigger ( struct snd_pcm_substream * substream , int cmd )
{
struct snd_dummy * dummy = snd_pcm_substream_chip ( substream ) ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
return dummy - > timer_ops - > start ( substream ) ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
return dummy - > timer_ops - > stop ( substream ) ;
}
return - EINVAL ;
}
static int dummy_pcm_prepare ( struct snd_pcm_substream * substream )
{
struct snd_dummy * dummy = snd_pcm_substream_chip ( substream ) ;
return dummy - > timer_ops - > prepare ( substream ) ;
}
static snd_pcm_uframes_t dummy_pcm_pointer ( struct snd_pcm_substream * substream )
{
struct snd_dummy * dummy = snd_pcm_substream_chip ( substream ) ;
return dummy - > timer_ops - > pointer ( substream ) ;
}
static struct snd_pcm_hardware dummy_pcm_hardware = {
. info = ( SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID ) ,
2005-04-16 15:20:36 -07:00
. formats = USE_FORMATS ,
. rates = USE_RATE ,
. rate_min = USE_RATE_MIN ,
. rate_max = USE_RATE_MAX ,
. channels_min = USE_CHANNELS_MIN ,
. channels_max = USE_CHANNELS_MAX ,
. buffer_bytes_max = MAX_BUFFER_SIZE ,
. period_bytes_min = 64 ,
2006-01-03 14:27:21 +01:00
. period_bytes_max = MAX_PERIOD_SIZE ,
2005-04-16 15:20:36 -07:00
. periods_min = USE_PERIODS_MIN ,
. periods_max = USE_PERIODS_MAX ,
. fifo_size = 0 ,
} ;
2009-09-03 15:59:26 +02:00
static int dummy_pcm_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * hw_params )
2005-04-16 15:20:36 -07:00
{
2009-09-04 12:19:36 +02:00
if ( fake_buffer ) {
/* runtime->dma_bytes has to be set manually to allow mmap */
substream - > runtime - > dma_bytes = params_buffer_bytes ( hw_params ) ;
return 0 ;
}
2009-09-03 15:59:26 +02:00
return snd_pcm_lib_malloc_pages ( substream ,
params_buffer_bytes ( hw_params ) ) ;
2005-04-16 15:20:36 -07:00
}
2009-09-03 15:59:26 +02:00
static int dummy_pcm_hw_free ( struct snd_pcm_substream * substream )
2005-04-16 15:20:36 -07:00
{
2009-09-04 12:19:36 +02:00
if ( fake_buffer )
return 0 ;
2005-04-16 15:20:36 -07:00
return snd_pcm_lib_free_pages ( substream ) ;
}
2009-09-03 15:59:26 +02:00
static int dummy_pcm_open ( struct snd_pcm_substream * substream )
2005-11-17 10:20:23 +01:00
{
2009-09-03 15:59:26 +02:00
struct snd_dummy * dummy = snd_pcm_substream_chip ( substream ) ;
2005-11-17 14:27:28 +01:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-11-17 10:20:23 +01:00
int err ;
2009-09-03 15:59:26 +02:00
dummy - > timer_ops = & dummy_systimer_ops ;
# ifdef CONFIG_HIGH_RES_TIMERS
if ( hrtimer )
dummy - > timer_ops = & dummy_hrtimer_ops ;
# endif
err = dummy - > timer_ops - > create ( substream ) ;
2008-08-15 12:59:02 +02:00
if ( err < 0 )
2005-04-16 15:20:36 -07:00
return err ;
2009-09-03 15:59:26 +02:00
runtime - > hw = dummy_pcm_hardware ;
if ( substream - > pcm - > device & 1 ) {
2005-04-16 15:20:36 -07:00
runtime - > hw . info & = ~ SNDRV_PCM_INFO_INTERLEAVED ;
runtime - > hw . info | = SNDRV_PCM_INFO_NONINTERLEAVED ;
}
if ( substream - > pcm - > device & 2 )
2009-09-03 15:59:26 +02:00
runtime - > hw . info & = ~ ( SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID ) ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
err = add_playback_constraints ( substream - > runtime ) ;
else
err = add_capture_constraints ( substream - > runtime ) ;
if ( err < 0 ) {
dummy - > timer_ops - > free ( substream ) ;
2005-04-16 15:20:36 -07:00
return err ;
2009-09-03 15:59:26 +02:00
}
2005-04-16 15:20:36 -07:00
return 0 ;
}
2009-09-03 15:59:26 +02:00
static int dummy_pcm_close ( struct snd_pcm_substream * substream )
2005-04-16 15:20:36 -07:00
{
2009-09-03 15:59:26 +02:00
struct snd_dummy * dummy = snd_pcm_substream_chip ( substream ) ;
dummy - > timer_ops - > free ( substream ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2009-09-04 12:19:36 +02:00
/*
* dummy buffer handling
*/
static void * dummy_page [ 2 ] ;
static void free_fake_buffer ( void )
{
if ( fake_buffer ) {
int i ;
for ( i = 0 ; i < 2 ; i + + )
if ( dummy_page [ i ] ) {
free_page ( ( unsigned long ) dummy_page [ i ] ) ;
dummy_page [ i ] = NULL ;
}
}
}
static int alloc_fake_buffer ( void )
{
int i ;
if ( ! fake_buffer )
return 0 ;
for ( i = 0 ; i < 2 ; i + + ) {
dummy_page [ i ] = ( void * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! dummy_page [ i ] ) {
free_fake_buffer ( ) ;
return - ENOMEM ;
}
}
return 0 ;
}
static int dummy_pcm_copy ( struct snd_pcm_substream * substream ,
int channel , snd_pcm_uframes_t pos ,
void __user * dst , snd_pcm_uframes_t count )
{
return 0 ; /* do nothing */
}
static int dummy_pcm_silence ( struct snd_pcm_substream * substream ,
int channel , snd_pcm_uframes_t pos ,
snd_pcm_uframes_t count )
{
return 0 ; /* do nothing */
}
static struct page * dummy_pcm_page ( struct snd_pcm_substream * substream ,
unsigned long offset )
{
return virt_to_page ( dummy_page [ substream - > stream ] ) ; /* the same page */
}
2009-09-03 15:59:26 +02:00
static struct snd_pcm_ops dummy_pcm_ops = {
. open = dummy_pcm_open ,
. close = dummy_pcm_close ,
. ioctl = snd_pcm_lib_ioctl ,
. hw_params = dummy_pcm_hw_params ,
. hw_free = dummy_pcm_hw_free ,
. prepare = dummy_pcm_prepare ,
. trigger = dummy_pcm_trigger ,
. pointer = dummy_pcm_pointer ,
2005-04-16 15:20:36 -07:00
} ;
2009-09-04 12:19:36 +02:00
static struct snd_pcm_ops dummy_pcm_ops_no_buf = {
. open = dummy_pcm_open ,
. close = dummy_pcm_close ,
. ioctl = snd_pcm_lib_ioctl ,
. hw_params = dummy_pcm_hw_params ,
. hw_free = dummy_pcm_hw_free ,
. prepare = dummy_pcm_prepare ,
. trigger = dummy_pcm_trigger ,
. pointer = dummy_pcm_pointer ,
. copy = dummy_pcm_copy ,
. silence = dummy_pcm_silence ,
. page = dummy_pcm_page ,
} ;
2007-02-13 13:11:11 +01:00
static int __devinit snd_card_dummy_pcm ( struct snd_dummy * dummy , int device ,
int substreams )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:27:28 +01:00
struct snd_pcm * pcm ;
2009-09-04 12:19:36 +02:00
struct snd_pcm_ops * ops ;
2005-04-16 15:20:36 -07:00
int err ;
2008-08-15 12:59:02 +02:00
err = snd_pcm_new ( dummy - > card , " Dummy PCM " , device ,
substreams , substreams , & pcm ) ;
if ( err < 0 )
2005-04-16 15:20:36 -07:00
return err ;
2005-11-17 16:01:56 +01:00
dummy - > pcm = pcm ;
2009-09-04 12:19:36 +02:00
if ( fake_buffer )
ops = & dummy_pcm_ops_no_buf ;
else
ops = & dummy_pcm_ops ;
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_PLAYBACK , ops ) ;
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_CAPTURE , ops ) ;
2005-04-16 15:20:36 -07:00
pcm - > private_data = dummy ;
pcm - > info_flags = 0 ;
strcpy ( pcm - > name , " Dummy PCM " ) ;
2009-09-04 12:19:36 +02:00
if ( ! fake_buffer ) {
snd_pcm_lib_preallocate_pages_for_all ( pcm ,
SNDRV_DMA_TYPE_CONTINUOUS ,
snd_dma_continuous_data ( GFP_KERNEL ) ,
0 , 64 * 1024 ) ;
}
2005-04-16 15:20:36 -07:00
return 0 ;
}
2009-09-08 14:30:49 +02:00
/*
* mixer interface
*/
2005-04-16 15:20:36 -07:00
# define DUMMY_VOLUME(xname, xindex, addr) \
2006-08-23 13:07:19 +02:00
{ . iface = SNDRV_CTL_ELEM_IFACE_MIXER , \
. access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ , \
. name = xname , . index = xindex , \
2005-04-16 15:20:36 -07:00
. info = snd_dummy_volume_info , \
. get = snd_dummy_volume_get , . put = snd_dummy_volume_put , \
2006-08-23 13:07:19 +02:00
. private_value = addr , \
. tlv = { . p = db_scale_dummy } }
2005-04-16 15:20:36 -07:00
2005-11-17 14:27:28 +01:00
static int snd_dummy_volume_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
2005-04-16 15:20:36 -07:00
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
uinfo - > count = 2 ;
uinfo - > value . integer . min = - 50 ;
uinfo - > value . integer . max = 100 ;
return 0 ;
}
2005-11-17 14:27:28 +01:00
static int snd_dummy_volume_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:27:28 +01:00
struct snd_dummy * dummy = snd_kcontrol_chip ( kcontrol ) ;
2005-04-16 15:20:36 -07:00
int addr = kcontrol - > private_value ;
2005-11-17 10:20:23 +01:00
spin_lock_irq ( & dummy - > mixer_lock ) ;
2005-04-16 15:20:36 -07:00
ucontrol - > value . integer . value [ 0 ] = dummy - > mixer_volume [ addr ] [ 0 ] ;
ucontrol - > value . integer . value [ 1 ] = dummy - > mixer_volume [ addr ] [ 1 ] ;
2005-11-17 10:20:23 +01:00
spin_unlock_irq ( & dummy - > mixer_lock ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2005-11-17 14:27:28 +01:00
static int snd_dummy_volume_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:27:28 +01:00
struct snd_dummy * dummy = snd_kcontrol_chip ( kcontrol ) ;
2005-04-16 15:20:36 -07:00
int change , addr = kcontrol - > private_value ;
int left , right ;
left = ucontrol - > value . integer . value [ 0 ] ;
if ( left < - 50 )
left = - 50 ;
if ( left > 100 )
left = 100 ;
right = ucontrol - > value . integer . value [ 1 ] ;
if ( right < - 50 )
right = - 50 ;
if ( right > 100 )
right = 100 ;
2005-11-17 10:20:23 +01:00
spin_lock_irq ( & dummy - > mixer_lock ) ;
2005-04-16 15:20:36 -07:00
change = dummy - > mixer_volume [ addr ] [ 0 ] ! = left | |
dummy - > mixer_volume [ addr ] [ 1 ] ! = right ;
dummy - > mixer_volume [ addr ] [ 0 ] = left ;
dummy - > mixer_volume [ addr ] [ 1 ] = right ;
2005-11-17 10:20:23 +01:00
spin_unlock_irq ( & dummy - > mixer_lock ) ;
2005-04-16 15:20:36 -07:00
return change ;
}
2007-01-29 15:33:49 +01:00
static const DECLARE_TLV_DB_SCALE ( db_scale_dummy , - 4500 , 30 , 0 ) ;
2006-08-23 13:07:19 +02:00
2005-04-16 15:20:36 -07:00
# define DUMMY_CAPSRC(xname, xindex, addr) \
{ . iface = SNDRV_CTL_ELEM_IFACE_MIXER , . name = xname , . index = xindex , \
. info = snd_dummy_capsrc_info , \
. get = snd_dummy_capsrc_get , . put = snd_dummy_capsrc_put , \
. private_value = addr }
2007-07-23 15:42:26 +02:00
# define snd_dummy_capsrc_info snd_ctl_boolean_stereo_info
2005-04-16 15:20:36 -07:00
2005-11-17 14:27:28 +01:00
static int snd_dummy_capsrc_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:27:28 +01:00
struct snd_dummy * dummy = snd_kcontrol_chip ( kcontrol ) ;
2005-04-16 15:20:36 -07:00
int addr = kcontrol - > private_value ;
2005-11-17 10:20:23 +01:00
spin_lock_irq ( & dummy - > mixer_lock ) ;
2005-04-16 15:20:36 -07:00
ucontrol - > value . integer . value [ 0 ] = dummy - > capture_source [ addr ] [ 0 ] ;
ucontrol - > value . integer . value [ 1 ] = dummy - > capture_source [ addr ] [ 1 ] ;
2005-11-17 10:20:23 +01:00
spin_unlock_irq ( & dummy - > mixer_lock ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2005-11-17 14:27:28 +01:00
static int snd_dummy_capsrc_put ( struct snd_kcontrol * kcontrol , struct snd_ctl_elem_value * ucontrol )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:27:28 +01:00
struct snd_dummy * dummy = snd_kcontrol_chip ( kcontrol ) ;
2005-04-16 15:20:36 -07:00
int change , addr = kcontrol - > private_value ;
int left , right ;
left = ucontrol - > value . integer . value [ 0 ] & 1 ;
right = ucontrol - > value . integer . value [ 1 ] & 1 ;
2005-11-17 10:20:23 +01:00
spin_lock_irq ( & dummy - > mixer_lock ) ;
2005-04-16 15:20:36 -07:00
change = dummy - > capture_source [ addr ] [ 0 ] ! = left & &
dummy - > capture_source [ addr ] [ 1 ] ! = right ;
dummy - > capture_source [ addr ] [ 0 ] = left ;
dummy - > capture_source [ addr ] [ 1 ] = right ;
2005-11-17 10:20:23 +01:00
spin_unlock_irq ( & dummy - > mixer_lock ) ;
2005-04-16 15:20:36 -07:00
return change ;
}
2005-11-17 14:27:28 +01:00
static struct snd_kcontrol_new snd_dummy_controls [ ] = {
2005-04-16 15:20:36 -07:00
DUMMY_VOLUME ( " Master Volume " , 0 , MIXER_ADDR_MASTER ) ,
DUMMY_CAPSRC ( " Master Capture Switch " , 0 , MIXER_ADDR_MASTER ) ,
DUMMY_VOLUME ( " Synth Volume " , 0 , MIXER_ADDR_SYNTH ) ,
2006-08-17 17:12:19 +02:00
DUMMY_CAPSRC ( " Synth Capture Switch " , 0 , MIXER_ADDR_SYNTH ) ,
2005-04-16 15:20:36 -07:00
DUMMY_VOLUME ( " Line Volume " , 0 , MIXER_ADDR_LINE ) ,
2006-08-17 17:12:19 +02:00
DUMMY_CAPSRC ( " Line Capture Switch " , 0 , MIXER_ADDR_LINE ) ,
2005-04-16 15:20:36 -07:00
DUMMY_VOLUME ( " Mic Volume " , 0 , MIXER_ADDR_MIC ) ,
2006-08-17 17:12:19 +02:00
DUMMY_CAPSRC ( " Mic Capture Switch " , 0 , MIXER_ADDR_MIC ) ,
2005-04-16 15:20:36 -07:00
DUMMY_VOLUME ( " CD Volume " , 0 , MIXER_ADDR_CD ) ,
2006-08-17 17:12:19 +02:00
DUMMY_CAPSRC ( " CD Capture Switch " , 0 , MIXER_ADDR_CD )
2005-04-16 15:20:36 -07:00
} ;
2007-02-13 13:11:11 +01:00
static int __devinit snd_card_dummy_new_mixer ( struct snd_dummy * dummy )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:27:28 +01:00
struct snd_card * card = dummy - > card ;
2005-04-16 15:20:36 -07:00
unsigned int idx ;
int err ;
spin_lock_init ( & dummy - > mixer_lock ) ;
strcpy ( card - > mixername , " Dummy Mixer " ) ;
for ( idx = 0 ; idx < ARRAY_SIZE ( snd_dummy_controls ) ; idx + + ) {
2008-08-15 12:59:02 +02:00
err = snd_ctl_add ( card , snd_ctl_new1 ( & snd_dummy_controls [ idx ] , dummy ) ) ;
if ( err < 0 )
2005-04-16 15:20:36 -07:00
return err ;
}
return 0 ;
}
2009-09-08 14:30:49 +02:00
# if defined(CONFIG_SND_DEBUG) && defined(CONFIG_PROC_FS)
/*
* proc interface
*/
static void print_formats ( struct snd_info_buffer * buffer )
{
int i ;
for ( i = 0 ; i < SNDRV_PCM_FORMAT_LAST ; i + + ) {
if ( dummy_pcm_hardware . formats & ( 1ULL < < i ) )
snd_iprintf ( buffer , " %s " , snd_pcm_format_name ( i ) ) ;
}
}
static void print_rates ( struct snd_info_buffer * buffer )
{
static int rates [ ] = {
5512 , 8000 , 11025 , 16000 , 22050 , 32000 , 44100 , 48000 ,
64000 , 88200 , 96000 , 176400 , 192000 ,
} ;
int i ;
if ( dummy_pcm_hardware . rates & SNDRV_PCM_RATE_CONTINUOUS )
snd_iprintf ( buffer , " continuous " ) ;
if ( dummy_pcm_hardware . rates & SNDRV_PCM_RATE_KNOT )
snd_iprintf ( buffer , " knot " ) ;
for ( i = 0 ; i < ARRAY_SIZE ( rates ) ; i + + )
if ( dummy_pcm_hardware . rates & ( 1 < < i ) )
snd_iprintf ( buffer , " %d " , rates [ i ] ) ;
}
# define get_dummy_int_ptr(ofs) \
( unsigned int * ) ( ( char * ) & dummy_pcm_hardware + ( ofs ) )
# define get_dummy_ll_ptr(ofs) \
( unsigned long long * ) ( ( char * ) & dummy_pcm_hardware + ( ofs ) )
struct dummy_hw_field {
const char * name ;
const char * format ;
unsigned int offset ;
unsigned int size ;
} ;
# define FIELD_ENTRY(item, fmt) { \
. name = # item , \
. format = fmt , \
. offset = offsetof ( struct snd_pcm_hardware , item ) , \
. size = sizeof ( dummy_pcm_hardware . item ) }
static struct dummy_hw_field fields [ ] = {
FIELD_ENTRY ( formats , " %#llx " ) ,
FIELD_ENTRY ( rates , " %#x " ) ,
FIELD_ENTRY ( rate_min , " %d " ) ,
FIELD_ENTRY ( rate_max , " %d " ) ,
FIELD_ENTRY ( channels_min , " %d " ) ,
FIELD_ENTRY ( channels_max , " %d " ) ,
FIELD_ENTRY ( buffer_bytes_max , " %ld " ) ,
FIELD_ENTRY ( period_bytes_min , " %ld " ) ,
FIELD_ENTRY ( period_bytes_max , " %ld " ) ,
FIELD_ENTRY ( periods_min , " %d " ) ,
FIELD_ENTRY ( periods_max , " %d " ) ,
} ;
static void dummy_proc_read ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( fields ) ; i + + ) {
snd_iprintf ( buffer , " %s " , fields [ i ] . name ) ;
if ( fields [ i ] . size = = sizeof ( int ) )
snd_iprintf ( buffer , fields [ i ] . format ,
* get_dummy_int_ptr ( fields [ i ] . offset ) ) ;
else
snd_iprintf ( buffer , fields [ i ] . format ,
* get_dummy_ll_ptr ( fields [ i ] . offset ) ) ;
if ( ! strcmp ( fields [ i ] . name , " formats " ) )
print_formats ( buffer ) ;
else if ( ! strcmp ( fields [ i ] . name , " rates " ) )
print_rates ( buffer ) ;
snd_iprintf ( buffer , " \n " ) ;
}
}
static void dummy_proc_write ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
{
char line [ 64 ] ;
while ( ! snd_info_get_line ( buffer , line , sizeof ( line ) ) ) {
char item [ 20 ] ;
const char * ptr ;
unsigned long long val ;
int i ;
ptr = snd_info_get_str ( item , line , sizeof ( item ) ) ;
for ( i = 0 ; i < ARRAY_SIZE ( fields ) ; i + + ) {
if ( ! strcmp ( item , fields [ i ] . name ) )
break ;
}
if ( i > = ARRAY_SIZE ( fields ) )
continue ;
snd_info_get_str ( item , ptr , sizeof ( item ) ) ;
if ( strict_strtoull ( item , 0 , & val ) )
continue ;
if ( fields [ i ] . size = = sizeof ( int ) )
* get_dummy_int_ptr ( fields [ i ] . offset ) = val ;
else
* get_dummy_ll_ptr ( fields [ i ] . offset ) = val ;
}
}
static void __devinit dummy_proc_init ( struct snd_dummy * chip )
{
struct snd_info_entry * entry ;
if ( ! snd_card_proc_new ( chip - > card , " dummy_pcm " , & entry ) ) {
snd_info_set_text_ops ( entry , chip , dummy_proc_read ) ;
entry - > c . text . write = dummy_proc_write ;
entry - > mode | = S_IWUSR ;
}
}
# else
# define dummy_proc_init(x)
# endif /* CONFIG_SND_DEBUG && CONFIG_PROC_FS */
2007-02-13 13:11:11 +01:00
static int __devinit snd_dummy_probe ( struct platform_device * devptr )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:27:28 +01:00
struct snd_card * card ;
struct snd_dummy * dummy ;
2005-04-16 15:20:36 -07:00
int idx , err ;
2005-11-17 16:01:56 +01:00
int dev = devptr - > id ;
2005-04-16 15:20:36 -07:00
2008-12-28 16:45:02 +01:00
err = snd_card_create ( index [ dev ] , id [ dev ] , THIS_MODULE ,
sizeof ( struct snd_dummy ) , & card ) ;
if ( err < 0 )
return err ;
2005-11-17 14:27:28 +01:00
dummy = card - > private_data ;
2005-04-16 15:20:36 -07:00
dummy - > card = card ;
for ( idx = 0 ; idx < MAX_PCM_DEVICES & & idx < pcm_devs [ dev ] ; idx + + ) {
if ( pcm_substreams [ dev ] < 1 )
pcm_substreams [ dev ] = 1 ;
if ( pcm_substreams [ dev ] > MAX_PCM_SUBSTREAMS )
pcm_substreams [ dev ] = MAX_PCM_SUBSTREAMS ;
2008-08-15 12:59:02 +02:00
err = snd_card_dummy_pcm ( dummy , idx , pcm_substreams [ dev ] ) ;
if ( err < 0 )
2005-04-16 15:20:36 -07:00
goto __nodev ;
}
2008-08-15 12:59:02 +02:00
err = snd_card_dummy_new_mixer ( dummy ) ;
if ( err < 0 )
2005-04-16 15:20:36 -07:00
goto __nodev ;
strcpy ( card - > driver , " Dummy " ) ;
strcpy ( card - > shortname , " Dummy " ) ;
sprintf ( card - > longname , " Dummy %i " , dev + 1 ) ;
2005-09-05 17:17:58 +02:00
2009-09-08 14:30:49 +02:00
dummy_proc_init ( dummy ) ;
2005-11-17 16:01:56 +01:00
snd_card_set_dev ( card , & devptr - > dev ) ;
2005-09-05 17:17:58 +02:00
2008-08-15 12:59:02 +02:00
err = snd_card_register ( card ) ;
if ( err = = 0 ) {
2005-11-17 16:01:56 +01:00
platform_set_drvdata ( devptr , card ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
__nodev :
snd_card_free ( card ) ;
return err ;
}
2007-02-13 13:11:11 +01:00
static int __devexit snd_dummy_remove ( struct platform_device * devptr )
2005-11-17 16:01:56 +01:00
{
snd_card_free ( platform_get_drvdata ( devptr ) ) ;
platform_set_drvdata ( devptr , NULL ) ;
return 0 ;
}
# ifdef CONFIG_PM
static int snd_dummy_suspend ( struct platform_device * pdev , pm_message_t state )
2005-04-16 15:20:36 -07:00
{
2005-11-17 16:01:56 +01:00
struct snd_card * card = platform_get_drvdata ( pdev ) ;
struct snd_dummy * dummy = card - > private_data ;
2005-04-16 15:20:36 -07:00
2005-11-17 16:01:56 +01:00
snd_power_change_state ( card , SNDRV_CTL_POWER_D3hot ) ;
snd_pcm_suspend_all ( dummy - > pcm ) ;
return 0 ;
}
static int snd_dummy_resume ( struct platform_device * pdev )
{
struct snd_card * card = platform_get_drvdata ( pdev ) ;
snd_power_change_state ( card , SNDRV_CTL_POWER_D0 ) ;
return 0 ;
}
# endif
# define SND_DUMMY_DRIVER "snd_dummy"
static struct platform_driver snd_dummy_driver = {
. probe = snd_dummy_probe ,
2007-02-13 13:11:11 +01:00
. remove = __devexit_p ( snd_dummy_remove ) ,
2005-11-17 16:01:56 +01:00
# ifdef CONFIG_PM
. suspend = snd_dummy_suspend ,
. resume = snd_dummy_resume ,
2005-04-16 15:20:36 -07:00
# endif
2005-11-17 16:01:56 +01:00
. driver = {
. name = SND_DUMMY_DRIVER
} ,
} ;
2007-06-25 12:08:01 +02:00
static void snd_dummy_unregister_all ( void )
2005-12-07 09:13:42 +01:00
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( devices ) ; + + i )
platform_device_unregister ( devices [ i ] ) ;
platform_driver_unregister ( & snd_dummy_driver ) ;
2009-09-04 12:19:36 +02:00
free_fake_buffer ( ) ;
2005-12-07 09:13:42 +01:00
}
2005-11-17 16:01:56 +01:00
static int __init alsa_card_dummy_init ( void )
{
int i , cards , err ;
2008-08-15 12:59:02 +02:00
err = platform_driver_register ( & snd_dummy_driver ) ;
if ( err < 0 )
2005-11-17 16:01:56 +01:00
return err ;
2009-09-04 12:19:36 +02:00
err = alloc_fake_buffer ( ) ;
if ( err < 0 ) {
platform_driver_unregister ( & snd_dummy_driver ) ;
return err ;
}
2005-11-17 16:01:56 +01:00
cards = 0 ;
2006-02-20 11:57:34 +01:00
for ( i = 0 ; i < SNDRV_CARDS ; i + + ) {
2005-11-17 16:01:56 +01:00
struct platform_device * device ;
2006-02-20 11:57:34 +01:00
if ( ! enable [ i ] )
continue ;
2005-11-17 16:01:56 +01:00
device = platform_device_register_simple ( SND_DUMMY_DRIVER ,
i , NULL , 0 ) ;
2006-04-13 12:57:11 +02:00
if ( IS_ERR ( device ) )
continue ;
2006-04-13 12:58:06 +02:00
if ( ! platform_get_drvdata ( device ) ) {
platform_device_unregister ( device ) ;
continue ;
}
2005-12-07 09:13:42 +01:00
devices [ i ] = device ;
2005-04-16 15:20:36 -07:00
cards + + ;
}
if ( ! cards ) {
# ifdef MODULE
printk ( KERN_ERR " Dummy soundcard not found or device busy \n " ) ;
# endif
2006-04-13 12:57:11 +02:00
snd_dummy_unregister_all ( ) ;
return - ENODEV ;
2005-04-16 15:20:36 -07:00
}
return 0 ;
}
static void __exit alsa_card_dummy_exit ( void )
{
2005-12-07 09:13:42 +01:00
snd_dummy_unregister_all ( ) ;
2005-04-16 15:20:36 -07:00
}
module_init ( alsa_card_dummy_init )
module_exit ( alsa_card_dummy_exit )