2019-05-27 09:55:05 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-17 02:20:36 +04:00
/*
* Dummy soundcard
2007-10-15 11:50:19 +04:00
* Copyright ( c ) by Jaroslav Kysela < perex @ perex . cz >
2005-04-17 02:20:36 +04:00
*/
# include <linux/init.h>
2005-11-17 18:01:56 +03:00
# include <linux/err.h>
# include <linux/platform_device.h>
2005-04-17 02:20:36 +04:00
# include <linux/jiffies.h>
# include <linux/slab.h>
# include <linux/time.h>
# include <linux/wait.h>
2009-09-03 17:59:26 +04:00
# include <linux/hrtimer.h>
# include <linux/math64.h>
2011-07-15 21:13:37 +04:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# include <sound/core.h>
# include <sound/control.h>
2006-08-23 15:07:19 +04:00
# include <sound/tlv.h>
2005-04-17 02:20:36 +04:00
# include <sound/pcm.h>
# include <sound/rawmidi.h>
2009-09-08 16:30:49 +04:00
# include <sound/info.h>
2005-04-17 02:20:36 +04:00
# include <sound/initval.h>
2007-10-15 11:50:19 +04:00
MODULE_AUTHOR ( " Jaroslav Kysela <perex@perex.cz> " ) ;
2005-04-17 02:20:36 +04:00
MODULE_DESCRIPTION ( " Dummy soundcard (/dev/null) " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define MAX_PCM_DEVICES 4
2009-09-08 20:15:17 +04:00
# define MAX_PCM_SUBSTREAMS 128
2005-04-17 02:20:36 +04:00
# define MAX_MIDI_DEVICES 2
/* defaults */
# define MAX_BUFFER_SIZE (64*1024)
2010-02-02 19:48:51 +03:00
# define MIN_PERIOD_SIZE 64
2006-01-03 16:27:21 +03:00
# define MAX_PERIOD_SIZE MAX_BUFFER_SIZE
2005-04-17 02:20:36 +04:00
# define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
# define USE_RATE SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000
# define USE_RATE_MIN 5500
# define USE_RATE_MAX 48000
# define USE_CHANNELS_MIN 1
# define USE_CHANNELS_MAX 2
# define USE_PERIODS_MIN 1
# define USE_PERIODS_MAX 1024
static int index [ SNDRV_CARDS ] = SNDRV_DEFAULT_IDX ; /* Index 0-MAX */
static char * id [ SNDRV_CARDS ] = SNDRV_DEFAULT_STR ; /* ID for this card */
2011-12-15 07:19:36 +04:00
static bool enable [ SNDRV_CARDS ] = { 1 , [ 1 . . . ( SNDRV_CARDS - 1 ) ] = 0 } ;
2010-02-02 19:48:51 +03:00
static char * model [ SNDRV_CARDS ] = { [ 0 . . . ( SNDRV_CARDS - 1 ) ] = NULL } ;
2005-04-17 02:20:36 +04:00
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 17:59:26 +04:00
# ifdef CONFIG_HIGH_RES_TIMERS
2011-12-15 07:19:36 +04:00
static bool hrtimer = 1 ;
2009-09-03 17:59:26 +04:00
# endif
2011-12-15 07:19:36 +04:00
static bool fake_buffer = 1 ;
2005-04-17 02:20:36 +04: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. " ) ;
2010-02-02 19:48:51 +03:00
module_param_array ( model , charp , NULL , 0444 ) ;
MODULE_PARM_DESC ( model , " Soundcard model. " ) ;
2005-04-17 02:20:36 +04:00
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 16:10:59 +03:00
MODULE_PARM_DESC ( pcm_substreams , " PCM substreams # (1-128) for dummy driver. " ) ;
2005-04-17 02:20:36 +04:00
//module_param_array(midi_devs, int, NULL, 0444);
//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver.");
2009-09-04 14:19:36 +04:00
module_param ( fake_buffer , bool , 0444 ) ;
MODULE_PARM_DESC ( fake_buffer , " Fake buffer allocations. " ) ;
2009-09-03 17:59:26 +04:00
# ifdef CONFIG_HIGH_RES_TIMERS
2016-02-02 17:27:36 +03:00
module_param ( hrtimer , bool , 0644 ) ;
2009-09-03 17:59:26 +04:00
MODULE_PARM_DESC ( hrtimer , " Use hrtimer as the timer source. " ) ;
# endif
2005-04-17 02:20:36 +04:00
2005-12-07 11:13:42 +03:00
static struct platform_device * devices [ SNDRV_CARDS ] ;
2005-04-17 02:20:36 +04: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 17:59:26 +04: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 * ) ;
} ;
2016-02-02 17:27:36 +03:00
# define get_dummy_ops(substream) \
( * ( const struct dummy_timer_ops * * ) ( substream ) - > runtime - > private_data )
2010-02-02 19:48:51 +03:00
struct dummy_model {
const char * name ;
int ( * playback_constraints ) ( struct snd_pcm_runtime * runtime ) ;
int ( * capture_constraints ) ( struct snd_pcm_runtime * runtime ) ;
u64 formats ;
size_t buffer_bytes_max ;
size_t period_bytes_min ;
size_t period_bytes_max ;
unsigned int periods_min ;
unsigned int periods_max ;
unsigned int rates ;
unsigned int rate_min ;
unsigned int rate_max ;
unsigned int channels_min ;
unsigned int channels_max ;
} ;
2005-11-17 16:27:28 +03:00
struct snd_dummy {
struct snd_card * card ;
2020-01-05 17:47:19 +03:00
const struct dummy_model * model ;
2005-11-17 18:01:56 +03:00
struct snd_pcm * pcm ;
2010-02-02 19:48:51 +03:00
struct snd_pcm_hardware pcm_hw ;
2005-04-17 02:20:36 +04:00
spinlock_t mixer_lock ;
int mixer_volume [ MIXER_ADDR_LAST + 1 ] [ 2 ] ;
int capture_source [ MIXER_ADDR_LAST + 1 ] [ 2 ] ;
2012-10-20 14:21:36 +04:00
int iobox ;
struct snd_kcontrol * cd_volume_ctl ;
struct snd_kcontrol * cd_switch_ctl ;
2005-11-17 16:27:28 +03:00
} ;
2005-04-17 02:20:36 +04:00
2010-02-02 19:48:51 +03:00
/*
* card models
*/
static int emu10k1_playback_constraints ( struct snd_pcm_runtime * runtime )
{
int err ;
err = snd_pcm_hw_constraint_integer ( runtime , SNDRV_PCM_HW_PARAM_PERIODS ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_constraint_minmax ( runtime , SNDRV_PCM_HW_PARAM_BUFFER_BYTES , 256 , UINT_MAX ) ;
if ( err < 0 )
return err ;
return 0 ;
}
2020-01-05 17:47:19 +03:00
static const struct dummy_model model_emu10k1 = {
2010-02-02 19:48:51 +03:00
. name = " emu10k1 " ,
. playback_constraints = emu10k1_playback_constraints ,
. buffer_bytes_max = 128 * 1024 ,
} ;
2020-01-05 17:47:19 +03:00
static const struct dummy_model model_rme9652 = {
2010-02-02 19:48:51 +03:00
. name = " rme9652 " ,
. buffer_bytes_max = 26 * 64 * 1024 ,
. formats = SNDRV_PCM_FMTBIT_S32_LE ,
. channels_min = 26 ,
. channels_max = 26 ,
. periods_min = 2 ,
. periods_max = 2 ,
} ;
2020-01-05 17:47:19 +03:00
static const struct dummy_model model_ice1712 = {
2010-02-02 19:48:51 +03:00
. name = " ice1712 " ,
. buffer_bytes_max = 256 * 1024 ,
. formats = SNDRV_PCM_FMTBIT_S32_LE ,
. channels_min = 10 ,
. channels_max = 10 ,
. periods_min = 1 ,
. periods_max = 1024 ,
} ;
2020-01-05 17:47:19 +03:00
static const struct dummy_model model_uda1341 = {
2010-02-02 19:48:51 +03:00
. name = " uda1341 " ,
. buffer_bytes_max = 16380 ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
. channels_min = 2 ,
. channels_max = 2 ,
. periods_min = 2 ,
. periods_max = 255 ,
} ;
2020-01-05 17:47:19 +03:00
static const struct dummy_model model_ac97 = {
2010-02-02 19:48:51 +03:00
. name = " ac97 " ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
. channels_min = 2 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_48000 ,
. rate_min = 48000 ,
. rate_max = 48000 ,
} ;
2020-01-05 17:47:19 +03:00
static const struct dummy_model model_ca0106 = {
2010-02-02 19:48:51 +03:00
. name = " ca0106 " ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
. buffer_bytes_max = ( ( 65536 - 64 ) * 8 ) ,
. period_bytes_max = ( 65536 - 64 ) ,
. periods_min = 2 ,
. periods_max = 8 ,
. channels_min = 2 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 ,
. rate_min = 48000 ,
. rate_max = 192000 ,
} ;
2020-01-05 17:47:19 +03:00
static const struct dummy_model * dummy_models [ ] = {
2010-02-02 19:48:51 +03:00
& model_emu10k1 ,
& model_rme9652 ,
& model_ice1712 ,
& model_uda1341 ,
& model_ac97 ,
& model_ca0106 ,
NULL
} ;
2009-09-03 17:59:26 +04:00
/*
* system timer interface
*/
struct dummy_systimer_pcm {
2016-02-02 17:27:36 +03:00
/* ops must be the first item */
const struct dummy_timer_ops * timer_ops ;
2005-04-17 02:20:36 +04:00
spinlock_t lock ;
struct timer_list timer ;
2009-09-03 18:01:06 +04:00
unsigned long base_time ;
unsigned int frac_pos ; /* fractional sample position (based HZ) */
2009-09-04 10:45:11 +04:00
unsigned int frac_period_rest ;
2009-09-03 18:01:06 +04:00
unsigned int frac_buffer_size ; /* buffer_size * HZ */
unsigned int frac_period_size ; /* period_size * HZ */
unsigned int rate ;
2009-09-04 10:45:11 +04:00
int elapsed ;
2005-11-17 16:27:28 +03:00
struct snd_pcm_substream * substream ;
} ;
2005-04-17 02:20:36 +04:00
2009-09-03 18:01:06 +04:00
static void dummy_systimer_rearm ( struct dummy_systimer_pcm * dpcm )
{
2015-01-19 12:42:54 +03:00
mod_timer ( & dpcm - > timer , jiffies +
2020-12-23 20:22:18 +03:00
DIV_ROUND_UP ( dpcm - > frac_period_rest , dpcm - > rate ) ) ;
2009-09-03 18:01:06 +04:00
}
static void dummy_systimer_update ( struct dummy_systimer_pcm * dpcm )
{
unsigned long delta ;
delta = jiffies - dpcm - > base_time ;
if ( ! delta )
return ;
2009-09-04 10:45:11 +04:00
dpcm - > base_time + = delta ;
delta * = dpcm - > rate ;
dpcm - > frac_pos + = delta ;
2009-09-03 18:01:06 +04:00
while ( dpcm - > frac_pos > = dpcm - > frac_buffer_size )
dpcm - > frac_pos - = dpcm - > frac_buffer_size ;
2009-09-04 10:45:11 +04: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 18:01:06 +04:00
}
2009-09-03 17:59:26 +04:00
static int dummy_systimer_start ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2009-09-03 17:59:26 +04:00
struct dummy_systimer_pcm * dpcm = substream - > runtime - > private_data ;
spin_lock ( & dpcm - > lock ) ;
2009-09-03 18:01:06 +04:00
dpcm - > base_time = jiffies ;
dummy_systimer_rearm ( dpcm ) ;
2009-09-03 17:59:26 +04:00
spin_unlock ( & dpcm - > lock ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-09-03 17:59:26 +04:00
static int dummy_systimer_stop ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2009-09-03 17:59:26 +04:00
struct dummy_systimer_pcm * dpcm = substream - > runtime - > private_data ;
2005-11-17 12:20:23 +03:00
spin_lock ( & dpcm - > lock ) ;
2009-09-03 17:59:26 +04:00
del_timer ( & dpcm - > timer ) ;
2005-11-17 12:20:23 +03:00
spin_unlock ( & dpcm - > lock ) ;
2005-11-17 18:01:56 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-09-03 17:59:26 +04:00
static int dummy_systimer_prepare ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:27:28 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2009-09-03 17:59:26 +04:00
struct dummy_systimer_pcm * dpcm = runtime - > private_data ;
2008-04-16 21:30:30 +04:00
2009-09-03 18:01:06 +04: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 10:45:11 +04:00
dpcm - > frac_period_rest = dpcm - > frac_period_size ;
dpcm - > elapsed = 0 ;
2008-02-21 09:55:30 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2017-10-24 18:34:29 +03:00
static void dummy_systimer_callback ( struct timer_list * t )
2005-04-17 02:20:36 +04:00
{
2017-10-24 18:34:29 +03:00
struct dummy_systimer_pcm * dpcm = from_timer ( dpcm , t , timer ) ;
2005-11-18 20:52:14 +03:00
unsigned long flags ;
2009-09-04 10:45:11 +04:00
int elapsed = 0 ;
2005-04-17 02:20:36 +04:00
2005-11-18 20:52:14 +03:00
spin_lock_irqsave ( & dpcm - > lock , flags ) ;
2009-09-03 18:01:06 +04:00
dummy_systimer_update ( dpcm ) ;
dummy_systimer_rearm ( dpcm ) ;
2009-09-04 10:45:11 +04:00
elapsed = dpcm - > elapsed ;
dpcm - > elapsed = 0 ;
2009-09-03 18:01:06 +04:00
spin_unlock_irqrestore ( & dpcm - > lock , flags ) ;
2009-09-04 10:45:11 +04:00
if ( elapsed )
snd_pcm_period_elapsed ( dpcm - > substream ) ;
2005-04-17 02:20:36 +04:00
}
2009-09-03 17:59:26 +04:00
static snd_pcm_uframes_t
dummy_systimer_pointer ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2009-09-03 18:01:06 +04:00
struct dummy_systimer_pcm * dpcm = substream - > runtime - > private_data ;
2009-09-04 10:45:11 +04:00
snd_pcm_uframes_t pos ;
2005-04-17 02:20:36 +04:00
2009-09-03 18:01:06 +04:00
spin_lock ( & dpcm - > lock ) ;
dummy_systimer_update ( dpcm ) ;
2009-09-04 10:45:11 +04:00
pos = dpcm - > frac_pos / HZ ;
2009-09-03 18:01:06 +04:00
spin_unlock ( & dpcm - > lock ) ;
2009-09-04 10:45:11 +04:00
return pos ;
2005-04-17 02:20:36 +04:00
}
2009-09-03 17:59:26 +04:00
static int dummy_systimer_create ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2009-09-03 17:59:26 +04:00
struct dummy_systimer_pcm * dpcm ;
dpcm = kzalloc ( sizeof ( * dpcm ) , GFP_KERNEL ) ;
if ( ! dpcm )
return - ENOMEM ;
substream - > runtime - > private_data = dpcm ;
2017-10-24 18:34:29 +03:00
timer_setup ( & dpcm - > timer , dummy_systimer_callback , 0 ) ;
2009-09-03 17:59:26 +04:00
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 ) ;
}
2015-12-30 14:28:49 +03:00
static const struct dummy_timer_ops dummy_systimer_ops = {
2009-09-03 17:59:26 +04:00
. 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-17 02:20:36 +04:00
} ;
2009-09-03 17:59:26 +04:00
# ifdef CONFIG_HIGH_RES_TIMERS
/*
* hrtimer interface
*/
struct dummy_hrtimer_pcm {
2016-02-02 17:27:36 +03:00
/* ops must be the first item */
const struct dummy_timer_ops * timer_ops ;
2009-09-03 17:59:26 +04:00
ktime_t base_time ;
ktime_t period_time ;
atomic_t running ;
struct hrtimer timer ;
struct snd_pcm_substream * 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 ;
2017-12-21 13:42:03 +03:00
/*
* In cases of XRUN and draining , this calls . trigger to stop PCM
* substream .
*/
snd_pcm_period_elapsed ( dpcm - > substream ) ;
if ( ! atomic_read ( & dpcm - > running ) )
return HRTIMER_NORESTART ;
2009-09-03 17:59:26 +04:00
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 ) ;
2017-12-21 13:42:03 +03:00
hrtimer_start ( & dpcm - > timer , dpcm - > period_time , HRTIMER_MODE_REL_SOFT ) ;
2009-09-03 17:59:26 +04:00
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 ) ;
2017-12-21 13:42:03 +03:00
if ( ! hrtimer_callback_running ( & dpcm - > timer ) )
hrtimer_cancel ( & dpcm - > timer ) ;
2009-09-03 17:59:26 +04:00
return 0 ;
}
static inline void dummy_hrtimer_sync ( struct dummy_hrtimer_pcm * dpcm )
{
2016-06-24 16:15:26 +03:00
hrtimer_cancel ( & dpcm - > timer ) ;
2009-09-03 17:59:26 +04:00
}
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-17 02:20:36 +04:00
{
2009-09-03 17:59:26 +04: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 ;
2017-12-21 13:42:03 +03:00
hrtimer_init ( & dpcm - > timer , CLOCK_MONOTONIC , HRTIMER_MODE_REL_SOFT ) ;
2009-09-03 17:59:26 +04:00
dpcm - > timer . function = dummy_hrtimer_callback ;
dpcm - > substream = substream ;
atomic_set ( & dpcm - > running , 0 ) ;
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 ) ;
}
2015-12-30 14:28:49 +03:00
static const struct dummy_timer_ops dummy_hrtimer_ops = {
2009-09-03 17:59:26 +04:00
. 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 )
{
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
2016-02-02 17:27:36 +03:00
return get_dummy_ops ( substream ) - > start ( substream ) ;
2009-09-03 17:59:26 +04:00
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
2016-02-02 17:27:36 +03:00
return get_dummy_ops ( substream ) - > stop ( substream ) ;
2009-09-03 17:59:26 +04:00
}
return - EINVAL ;
}
static int dummy_pcm_prepare ( struct snd_pcm_substream * substream )
{
2016-02-02 17:27:36 +03:00
return get_dummy_ops ( substream ) - > prepare ( substream ) ;
2009-09-03 17:59:26 +04:00
}
static snd_pcm_uframes_t dummy_pcm_pointer ( struct snd_pcm_substream * substream )
{
2016-02-02 17:27:36 +03:00
return get_dummy_ops ( substream ) - > pointer ( substream ) ;
2009-09-03 17:59:26 +04:00
}
2017-08-17 12:15:51 +03:00
static const struct snd_pcm_hardware dummy_pcm_hardware = {
2009-09-03 17:59:26 +04:00
. info = ( SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID ) ,
2005-04-17 02:20:36 +04: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 ,
2010-02-02 19:48:51 +03:00
. period_bytes_min = MIN_PERIOD_SIZE ,
2006-01-03 16:27:21 +03:00
. period_bytes_max = MAX_PERIOD_SIZE ,
2005-04-17 02:20:36 +04:00
. periods_min = USE_PERIODS_MIN ,
. periods_max = USE_PERIODS_MAX ,
. fifo_size = 0 ,
} ;
2009-09-03 17:59:26 +04:00
static int dummy_pcm_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * hw_params )
2005-04-17 02:20:36 +04:00
{
2009-09-04 14:19:36 +04: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 ;
}
2019-12-09 12:48:37 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-09-03 17:59:26 +04:00
static int dummy_pcm_open ( struct snd_pcm_substream * substream )
2005-11-17 12:20:23 +03:00
{
2009-09-03 17:59:26 +04:00
struct snd_dummy * dummy = snd_pcm_substream_chip ( substream ) ;
2020-01-05 17:47:19 +03:00
const struct dummy_model * model = dummy - > model ;
2005-11-17 16:27:28 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2016-02-02 17:27:36 +03:00
const struct dummy_timer_ops * ops ;
2005-11-17 12:20:23 +03:00
int err ;
2016-02-02 17:27:36 +03:00
ops = & dummy_systimer_ops ;
2009-09-03 17:59:26 +04:00
# ifdef CONFIG_HIGH_RES_TIMERS
if ( hrtimer )
2016-02-02 17:27:36 +03:00
ops = & dummy_hrtimer_ops ;
2009-09-03 17:59:26 +04:00
# endif
2016-02-02 17:27:36 +03:00
err = ops - > create ( substream ) ;
2008-08-15 14:59:02 +04:00
if ( err < 0 )
2005-04-17 02:20:36 +04:00
return err ;
2016-02-02 17:27:36 +03:00
get_dummy_ops ( substream ) = ops ;
2005-04-17 02:20:36 +04:00
2010-02-02 19:48:51 +03:00
runtime - > hw = dummy - > pcm_hw ;
2009-09-03 17:59:26 +04:00
if ( substream - > pcm - > device & 1 ) {
2005-04-17 02:20:36 +04:00
runtime - > hw . info & = ~ SNDRV_PCM_INFO_INTERLEAVED ;
runtime - > hw . info | = SNDRV_PCM_INFO_NONINTERLEAVED ;
}
if ( substream - > pcm - > device & 2 )
2009-09-03 17:59:26 +04:00
runtime - > hw . info & = ~ ( SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID ) ;
2010-02-02 19:48:51 +03:00
if ( model = = NULL )
return 0 ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
if ( model - > playback_constraints )
err = model - > playback_constraints ( substream - > runtime ) ;
} else {
if ( model - > capture_constraints )
err = model - > capture_constraints ( substream - > runtime ) ;
}
2009-09-03 17:59:26 +04:00
if ( err < 0 ) {
2016-02-02 17:27:36 +03:00
get_dummy_ops ( substream ) - > free ( substream ) ;
2005-04-17 02:20:36 +04:00
return err ;
2009-09-03 17:59:26 +04:00
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-09-03 17:59:26 +04:00
static int dummy_pcm_close ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2016-02-02 17:27:36 +03:00
get_dummy_ops ( substream ) - > free ( substream ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-09-04 14:19:36 +04: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 ,
2017-05-10 21:15:40 +03:00
int channel , unsigned long pos ,
void __user * dst , unsigned long bytes )
{
return 0 ; /* do nothing */
}
static int dummy_pcm_copy_kernel ( struct snd_pcm_substream * substream ,
int channel , unsigned long pos ,
void * dst , unsigned long bytes )
2009-09-04 14:19:36 +04:00
{
return 0 ; /* do nothing */
}
static int dummy_pcm_silence ( struct snd_pcm_substream * substream ,
2017-05-10 21:15:40 +03:00
int channel , unsigned long pos ,
unsigned long bytes )
2009-09-04 14:19:36 +04:00
{
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 */
}
2020-01-03 11:16:59 +03:00
static const struct snd_pcm_ops dummy_pcm_ops = {
2009-09-03 17:59:26 +04:00
. open = dummy_pcm_open ,
. close = dummy_pcm_close ,
. hw_params = dummy_pcm_hw_params ,
. prepare = dummy_pcm_prepare ,
. trigger = dummy_pcm_trigger ,
. pointer = dummy_pcm_pointer ,
2005-04-17 02:20:36 +04:00
} ;
2020-01-03 11:16:59 +03:00
static const struct snd_pcm_ops dummy_pcm_ops_no_buf = {
2009-09-04 14:19:36 +04:00
. open = dummy_pcm_open ,
. close = dummy_pcm_close ,
. hw_params = dummy_pcm_hw_params ,
. prepare = dummy_pcm_prepare ,
. trigger = dummy_pcm_trigger ,
. pointer = dummy_pcm_pointer ,
2017-05-10 21:15:40 +03:00
. copy_user = dummy_pcm_copy ,
. copy_kernel = dummy_pcm_copy_kernel ,
. fill_silence = dummy_pcm_silence ,
2009-09-04 14:19:36 +04:00
. page = dummy_pcm_page ,
} ;
2012-12-06 21:35:27 +04:00
static int snd_card_dummy_pcm ( struct snd_dummy * dummy , int device ,
int substreams )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:27:28 +03:00
struct snd_pcm * pcm ;
2020-01-03 11:16:59 +03:00
const struct snd_pcm_ops * ops ;
2005-04-17 02:20:36 +04:00
int err ;
2008-08-15 14:59:02 +04:00
err = snd_pcm_new ( dummy - > card , " Dummy PCM " , device ,
substreams , substreams , & pcm ) ;
if ( err < 0 )
2005-04-17 02:20:36 +04:00
return err ;
2005-11-17 18:01:56 +03:00
dummy - > pcm = pcm ;
2009-09-04 14:19:36 +04: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-17 02:20:36 +04:00
pcm - > private_data = dummy ;
pcm - > info_flags = 0 ;
strcpy ( pcm - > name , " Dummy PCM " ) ;
2009-09-04 14:19:36 +04:00
if ( ! fake_buffer ) {
2019-12-09 12:48:37 +03:00
snd_pcm_set_managed_buffer_all ( pcm ,
2009-09-04 14:19:36 +04:00
SNDRV_DMA_TYPE_CONTINUOUS ,
2019-11-05 18:18:33 +03:00
NULL ,
2009-09-04 14:19:36 +04:00
0 , 64 * 1024 ) ;
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-09-08 16:30:49 +04:00
/*
* mixer interface
*/
2005-04-17 02:20:36 +04:00
# define DUMMY_VOLUME(xname, xindex, addr) \
2006-08-23 15:07:19 +04: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-17 02:20:36 +04:00
. info = snd_dummy_volume_info , \
. get = snd_dummy_volume_get , . put = snd_dummy_volume_put , \
2006-08-23 15:07:19 +04:00
. private_value = addr , \
. tlv = { . p = db_scale_dummy } }
2005-04-17 02:20:36 +04:00
2005-11-17 16:27:28 +03:00
static int snd_dummy_volume_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
2005-04-17 02:20:36 +04: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 16:27:28 +03:00
static int snd_dummy_volume_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:27:28 +03:00
struct snd_dummy * dummy = snd_kcontrol_chip ( kcontrol ) ;
2005-04-17 02:20:36 +04:00
int addr = kcontrol - > private_value ;
2005-11-17 12:20:23 +03:00
spin_lock_irq ( & dummy - > mixer_lock ) ;
2005-04-17 02:20:36 +04:00
ucontrol - > value . integer . value [ 0 ] = dummy - > mixer_volume [ addr ] [ 0 ] ;
ucontrol - > value . integer . value [ 1 ] = dummy - > mixer_volume [ addr ] [ 1 ] ;
2005-11-17 12:20:23 +03:00
spin_unlock_irq ( & dummy - > mixer_lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-17 16:27:28 +03:00
static int snd_dummy_volume_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:27:28 +03:00
struct snd_dummy * dummy = snd_kcontrol_chip ( kcontrol ) ;
2005-04-17 02:20:36 +04: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 12:20:23 +03:00
spin_lock_irq ( & dummy - > mixer_lock ) ;
2005-04-17 02:20:36 +04: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 12:20:23 +03:00
spin_unlock_irq ( & dummy - > mixer_lock ) ;
2005-04-17 02:20:36 +04:00
return change ;
}
2007-01-29 17:33:49 +03:00
static const DECLARE_TLV_DB_SCALE ( db_scale_dummy , - 4500 , 30 , 0 ) ;
2006-08-23 15:07:19 +04:00
2005-04-17 02:20:36 +04: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 17:42:26 +04:00
# define snd_dummy_capsrc_info snd_ctl_boolean_stereo_info
2005-04-17 02:20:36 +04:00
2005-11-17 16:27:28 +03:00
static int snd_dummy_capsrc_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:27:28 +03:00
struct snd_dummy * dummy = snd_kcontrol_chip ( kcontrol ) ;
2005-04-17 02:20:36 +04:00
int addr = kcontrol - > private_value ;
2005-11-17 12:20:23 +03:00
spin_lock_irq ( & dummy - > mixer_lock ) ;
2005-04-17 02:20:36 +04:00
ucontrol - > value . integer . value [ 0 ] = dummy - > capture_source [ addr ] [ 0 ] ;
ucontrol - > value . integer . value [ 1 ] = dummy - > capture_source [ addr ] [ 1 ] ;
2005-11-17 12:20:23 +03:00
spin_unlock_irq ( & dummy - > mixer_lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-17 16:27:28 +03:00
static int snd_dummy_capsrc_put ( struct snd_kcontrol * kcontrol , struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:27:28 +03:00
struct snd_dummy * dummy = snd_kcontrol_chip ( kcontrol ) ;
2005-04-17 02:20:36 +04: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 12:20:23 +03:00
spin_lock_irq ( & dummy - > mixer_lock ) ;
2005-04-17 02:20:36 +04: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 12:20:23 +03:00
spin_unlock_irq ( & dummy - > mixer_lock ) ;
2005-04-17 02:20:36 +04:00
return change ;
}
2012-10-20 14:21:36 +04:00
static int snd_dummy_iobox_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * info )
{
2017-11-27 15:58:51 +03:00
static const char * const names [ ] = { " None " , " CD Player " } ;
2012-10-20 14:21:36 +04:00
return snd_ctl_enum_info ( info , 1 , 2 , names ) ;
}
static int snd_dummy_iobox_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * value )
{
struct snd_dummy * dummy = snd_kcontrol_chip ( kcontrol ) ;
value - > value . enumerated . item [ 0 ] = dummy - > iobox ;
return 0 ;
}
static int snd_dummy_iobox_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * value )
{
struct snd_dummy * dummy = snd_kcontrol_chip ( kcontrol ) ;
int changed ;
if ( value - > value . enumerated . item [ 0 ] > 1 )
return - EINVAL ;
changed = value - > value . enumerated . item [ 0 ] ! = dummy - > iobox ;
if ( changed ) {
dummy - > iobox = value - > value . enumerated . item [ 0 ] ;
if ( dummy - > iobox ) {
dummy - > cd_volume_ctl - > vd [ 0 ] . access & =
~ SNDRV_CTL_ELEM_ACCESS_INACTIVE ;
dummy - > cd_switch_ctl - > vd [ 0 ] . access & =
~ SNDRV_CTL_ELEM_ACCESS_INACTIVE ;
} else {
dummy - > cd_volume_ctl - > vd [ 0 ] . access | =
SNDRV_CTL_ELEM_ACCESS_INACTIVE ;
dummy - > cd_switch_ctl - > vd [ 0 ] . access | =
SNDRV_CTL_ELEM_ACCESS_INACTIVE ;
}
snd_ctl_notify ( dummy - > card , SNDRV_CTL_EVENT_MASK_INFO ,
& dummy - > cd_volume_ctl - > id ) ;
snd_ctl_notify ( dummy - > card , SNDRV_CTL_EVENT_MASK_INFO ,
& dummy - > cd_switch_ctl - > id ) ;
}
return changed ;
}
2020-01-03 11:16:49 +03:00
static const struct snd_kcontrol_new snd_dummy_controls [ ] = {
2005-04-17 02:20:36 +04: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 19:12:19 +04:00
DUMMY_CAPSRC ( " Synth Capture Switch " , 0 , MIXER_ADDR_SYNTH ) ,
2005-04-17 02:20:36 +04:00
DUMMY_VOLUME ( " Line Volume " , 0 , MIXER_ADDR_LINE ) ,
2006-08-17 19:12:19 +04:00
DUMMY_CAPSRC ( " Line Capture Switch " , 0 , MIXER_ADDR_LINE ) ,
2005-04-17 02:20:36 +04:00
DUMMY_VOLUME ( " Mic Volume " , 0 , MIXER_ADDR_MIC ) ,
2006-08-17 19:12:19 +04:00
DUMMY_CAPSRC ( " Mic Capture Switch " , 0 , MIXER_ADDR_MIC ) ,
2005-04-17 02:20:36 +04:00
DUMMY_VOLUME ( " CD Volume " , 0 , MIXER_ADDR_CD ) ,
2012-10-20 14:21:36 +04:00
DUMMY_CAPSRC ( " CD Capture Switch " , 0 , MIXER_ADDR_CD ) ,
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " External I/O Box " ,
. info = snd_dummy_iobox_info ,
. get = snd_dummy_iobox_get ,
. put = snd_dummy_iobox_put ,
} ,
2005-04-17 02:20:36 +04:00
} ;
2012-12-06 21:35:27 +04:00
static int snd_card_dummy_new_mixer ( struct snd_dummy * dummy )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:27:28 +03:00
struct snd_card * card = dummy - > card ;
2012-10-20 14:21:36 +04:00
struct snd_kcontrol * kcontrol ;
2005-04-17 02:20:36 +04:00
unsigned int idx ;
int err ;
spin_lock_init ( & dummy - > mixer_lock ) ;
strcpy ( card - > mixername , " Dummy Mixer " ) ;
2012-10-20 14:21:36 +04:00
dummy - > iobox = 1 ;
2005-04-17 02:20:36 +04:00
for ( idx = 0 ; idx < ARRAY_SIZE ( snd_dummy_controls ) ; idx + + ) {
2012-10-20 14:21:36 +04:00
kcontrol = snd_ctl_new1 ( & snd_dummy_controls [ idx ] , dummy ) ;
err = snd_ctl_add ( card , kcontrol ) ;
2008-08-15 14:59:02 +04:00
if ( err < 0 )
2005-04-17 02:20:36 +04:00
return err ;
2012-10-20 14:21:36 +04:00
if ( ! strcmp ( kcontrol - > id . name , " CD Volume " ) )
dummy - > cd_volume_ctl = kcontrol ;
else if ( ! strcmp ( kcontrol - > id . name , " CD Capture Switch " ) )
dummy - > cd_switch_ctl = kcontrol ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2015-05-29 08:47:50 +03:00
# if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_PROC_FS)
2009-09-08 16:30:49 +04:00
/*
* proc interface
*/
2010-02-02 19:48:51 +03:00
static void print_formats ( struct snd_dummy * dummy ,
struct snd_info_buffer * buffer )
2009-09-08 16:30:49 +04:00
{
2020-02-06 19:39:41 +03:00
snd_pcm_format_t i ;
2009-09-08 16:30:49 +04:00
2020-02-06 19:39:41 +03:00
pcm_for_each_format ( i ) {
if ( dummy - > pcm_hw . formats & pcm_format_to_bits ( i ) )
2009-09-08 16:30:49 +04:00
snd_iprintf ( buffer , " %s " , snd_pcm_format_name ( i ) ) ;
}
}
2010-02-02 19:48:51 +03:00
static void print_rates ( struct snd_dummy * dummy ,
struct snd_info_buffer * buffer )
2009-09-08 16:30:49 +04:00
{
2020-01-05 17:47:19 +03:00
static const int rates [ ] = {
2009-09-08 16:30:49 +04:00
5512 , 8000 , 11025 , 16000 , 22050 , 32000 , 44100 , 48000 ,
64000 , 88200 , 96000 , 176400 , 192000 ,
} ;
int i ;
2010-02-02 19:48:51 +03:00
if ( dummy - > pcm_hw . rates & SNDRV_PCM_RATE_CONTINUOUS )
2009-09-08 16:30:49 +04:00
snd_iprintf ( buffer , " continuous " ) ;
2010-02-02 19:48:51 +03:00
if ( dummy - > pcm_hw . rates & SNDRV_PCM_RATE_KNOT )
2009-09-08 16:30:49 +04:00
snd_iprintf ( buffer , " knot " ) ;
for ( i = 0 ; i < ARRAY_SIZE ( rates ) ; i + + )
2010-02-02 19:48:51 +03:00
if ( dummy - > pcm_hw . rates & ( 1 < < i ) )
2009-09-08 16:30:49 +04:00
snd_iprintf ( buffer , " %d " , rates [ i ] ) ;
}
2010-02-02 19:48:51 +03:00
# define get_dummy_int_ptr(dummy, ofs) \
( unsigned int * ) ( ( char * ) & ( ( dummy ) - > pcm_hw ) + ( ofs ) )
# define get_dummy_ll_ptr(dummy, ofs) \
( unsigned long long * ) ( ( char * ) & ( ( dummy ) - > pcm_hw ) + ( ofs ) )
2009-09-08 16:30:49 +04:00
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 ) }
2020-01-05 17:47:19 +03:00
static const struct dummy_hw_field fields [ ] = {
2009-09-08 16:30:49 +04:00
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 )
{
2010-02-02 19:48:51 +03:00
struct snd_dummy * dummy = entry - > private_data ;
2009-09-08 16:30:49 +04:00
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 ,
2010-02-02 19:48:51 +03:00
* get_dummy_int_ptr ( dummy , fields [ i ] . offset ) ) ;
2009-09-08 16:30:49 +04:00
else
snd_iprintf ( buffer , fields [ i ] . format ,
2010-02-02 19:48:51 +03:00
* get_dummy_ll_ptr ( dummy , fields [ i ] . offset ) ) ;
2009-09-08 16:30:49 +04:00
if ( ! strcmp ( fields [ i ] . name , " formats " ) )
2010-02-02 19:48:51 +03:00
print_formats ( dummy , buffer ) ;
2009-09-08 16:30:49 +04:00
else if ( ! strcmp ( fields [ i ] . name , " rates " ) )
2010-02-02 19:48:51 +03:00
print_rates ( dummy , buffer ) ;
2009-09-08 16:30:49 +04:00
snd_iprintf ( buffer , " \n " ) ;
}
}
static void dummy_proc_write ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
{
2010-02-02 19:48:51 +03:00
struct snd_dummy * dummy = entry - > private_data ;
2009-09-08 16:30:49 +04:00
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 ) ) ;
2013-07-19 11:24:59 +04:00
if ( kstrtoull ( item , 0 , & val ) )
2009-09-08 16:30:49 +04:00
continue ;
if ( fields [ i ] . size = = sizeof ( int ) )
2010-02-02 19:48:51 +03:00
* get_dummy_int_ptr ( dummy , fields [ i ] . offset ) = val ;
2009-09-08 16:30:49 +04:00
else
2010-02-02 19:48:51 +03:00
* get_dummy_ll_ptr ( dummy , fields [ i ] . offset ) = val ;
2009-09-08 16:30:49 +04:00
}
}
2012-12-06 21:35:27 +04:00
static void dummy_proc_init ( struct snd_dummy * chip )
2009-09-08 16:30:49 +04:00
{
2019-02-04 17:58:33 +03:00
snd_card_rw_proc_new ( chip - > card , " dummy_pcm " , chip ,
dummy_proc_read , dummy_proc_write ) ;
2009-09-08 16:30:49 +04:00
}
# else
# define dummy_proc_init(x)
2015-05-29 08:47:50 +03:00
# endif /* CONFIG_SND_DEBUG && CONFIG_SND_PROC_FS */
2009-09-08 16:30:49 +04:00
2012-12-06 21:35:27 +04:00
static int snd_dummy_probe ( struct platform_device * devptr )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:27:28 +03:00
struct snd_card * card ;
struct snd_dummy * dummy ;
2020-01-05 17:47:19 +03:00
const struct dummy_model * m = NULL , * * mdl ;
2005-04-17 02:20:36 +04:00
int idx , err ;
2005-11-17 18:01:56 +03:00
int dev = devptr - > id ;
2005-04-17 02:20:36 +04:00
2021-07-15 10:59:40 +03:00
err = snd_devm_card_new ( & devptr - > dev , index [ dev ] , id [ dev ] , THIS_MODULE ,
sizeof ( struct snd_dummy ) , & card ) ;
2008-12-28 18:45:02 +03:00
if ( err < 0 )
return err ;
2005-11-17 16:27:28 +03:00
dummy = card - > private_data ;
2005-04-17 02:20:36 +04:00
dummy - > card = card ;
2010-02-02 19:48:51 +03:00
for ( mdl = dummy_models ; * mdl & & model [ dev ] ; mdl + + ) {
if ( strcmp ( model [ dev ] , ( * mdl ) - > name ) = = 0 ) {
printk ( KERN_INFO
" snd-dummy: Using model '%s' for card %i \n " ,
( * mdl ) - > name , card - > number ) ;
m = dummy - > model = * mdl ;
break ;
}
}
2005-04-17 02:20:36 +04:00
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 14:59:02 +04:00
err = snd_card_dummy_pcm ( dummy , idx , pcm_substreams [ dev ] ) ;
if ( err < 0 )
2021-07-15 10:59:40 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}
2010-02-02 19:48:51 +03:00
dummy - > pcm_hw = dummy_pcm_hardware ;
if ( m ) {
if ( m - > formats )
dummy - > pcm_hw . formats = m - > formats ;
if ( m - > buffer_bytes_max )
dummy - > pcm_hw . buffer_bytes_max = m - > buffer_bytes_max ;
if ( m - > period_bytes_min )
dummy - > pcm_hw . period_bytes_min = m - > period_bytes_min ;
if ( m - > period_bytes_max )
dummy - > pcm_hw . period_bytes_max = m - > period_bytes_max ;
if ( m - > periods_min )
dummy - > pcm_hw . periods_min = m - > periods_min ;
if ( m - > periods_max )
dummy - > pcm_hw . periods_max = m - > periods_max ;
if ( m - > rates )
dummy - > pcm_hw . rates = m - > rates ;
if ( m - > rate_min )
dummy - > pcm_hw . rate_min = m - > rate_min ;
if ( m - > rate_max )
dummy - > pcm_hw . rate_max = m - > rate_max ;
if ( m - > channels_min )
dummy - > pcm_hw . channels_min = m - > channels_min ;
if ( m - > channels_max )
dummy - > pcm_hw . channels_max = m - > channels_max ;
}
2008-08-15 14:59:02 +04:00
err = snd_card_dummy_new_mixer ( dummy ) ;
if ( err < 0 )
2021-07-15 10:59:40 +03:00
return err ;
2005-04-17 02:20:36 +04:00
strcpy ( card - > driver , " Dummy " ) ;
strcpy ( card - > shortname , " Dummy " ) ;
sprintf ( card - > longname , " Dummy %i " , dev + 1 ) ;
2005-09-05 19:17:58 +04:00
2009-09-08 16:30:49 +04:00
dummy_proc_init ( dummy ) ;
2008-08-15 14:59:02 +04:00
err = snd_card_register ( card ) ;
2021-07-15 10:59:40 +03:00
if ( err < 0 )
return err ;
platform_set_drvdata ( devptr , card ) ;
2005-11-17 18:01:56 +03:00
return 0 ;
}
2012-08-09 17:47:15 +04:00
# ifdef CONFIG_PM_SLEEP
2012-07-02 13:22:40 +04:00
static int snd_dummy_suspend ( struct device * pdev )
2005-04-17 02:20:36 +04:00
{
2012-07-02 13:22:40 +04:00
struct snd_card * card = dev_get_drvdata ( pdev ) ;
2005-04-17 02:20:36 +04:00
2005-11-17 18:01:56 +03:00
snd_power_change_state ( card , SNDRV_CTL_POWER_D3hot ) ;
return 0 ;
}
2012-07-02 13:22:40 +04:00
static int snd_dummy_resume ( struct device * pdev )
2005-11-17 18:01:56 +03:00
{
2012-07-02 13:22:40 +04:00
struct snd_card * card = dev_get_drvdata ( pdev ) ;
2005-11-17 18:01:56 +03:00
snd_power_change_state ( card , SNDRV_CTL_POWER_D0 ) ;
return 0 ;
}
2012-07-02 13:22:40 +04:00
static SIMPLE_DEV_PM_OPS ( snd_dummy_pm , snd_dummy_suspend , snd_dummy_resume ) ;
# define SND_DUMMY_PM_OPS &snd_dummy_pm
# else
# define SND_DUMMY_PM_OPS NULL
2005-11-17 18:01:56 +03:00
# endif
# define SND_DUMMY_DRIVER "snd_dummy"
static struct platform_driver snd_dummy_driver = {
. probe = snd_dummy_probe ,
. driver = {
2012-07-02 12:50:24 +04:00
. name = SND_DUMMY_DRIVER ,
2012-07-02 13:22:40 +04:00
. pm = SND_DUMMY_PM_OPS ,
2005-11-17 18:01:56 +03:00
} ,
} ;
2007-06-25 14:08:01 +04:00
static void snd_dummy_unregister_all ( void )
2005-12-07 11:13:42 +03: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 14:19:36 +04:00
free_fake_buffer ( ) ;
2005-12-07 11:13:42 +03:00
}
2005-11-17 18:01:56 +03:00
static int __init alsa_card_dummy_init ( void )
{
int i , cards , err ;
2008-08-15 14:59:02 +04:00
err = platform_driver_register ( & snd_dummy_driver ) ;
if ( err < 0 )
2005-11-17 18:01:56 +03:00
return err ;
2009-09-04 14:19:36 +04:00
err = alloc_fake_buffer ( ) ;
if ( err < 0 ) {
platform_driver_unregister ( & snd_dummy_driver ) ;
return err ;
}
2005-11-17 18:01:56 +03:00
cards = 0 ;
2006-02-20 13:57:34 +03:00
for ( i = 0 ; i < SNDRV_CARDS ; i + + ) {
2005-11-17 18:01:56 +03:00
struct platform_device * device ;
2006-02-20 13:57:34 +03:00
if ( ! enable [ i ] )
continue ;
2005-11-17 18:01:56 +03:00
device = platform_device_register_simple ( SND_DUMMY_DRIVER ,
i , NULL , 0 ) ;
2006-04-13 14:57:11 +04:00
if ( IS_ERR ( device ) )
continue ;
2006-04-13 14:58:06 +04:00
if ( ! platform_get_drvdata ( device ) ) {
platform_device_unregister ( device ) ;
continue ;
}
2005-12-07 11:13:42 +03:00
devices [ i ] = device ;
2005-04-17 02:20:36 +04:00
cards + + ;
}
if ( ! cards ) {
# ifdef MODULE
printk ( KERN_ERR " Dummy soundcard not found or device busy \n " ) ;
# endif
2006-04-13 14:57:11 +04:00
snd_dummy_unregister_all ( ) ;
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
static void __exit alsa_card_dummy_exit ( void )
{
2005-12-07 11:13:42 +03:00
snd_dummy_unregister_all ( ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( alsa_card_dummy_init )
module_exit ( alsa_card_dummy_exit )