2005-04-17 02:20:36 +04:00
/*
* Digital Audio ( PCM ) abstract layer
2007-10-15 11:50:19 +04:00
* Copyright ( c ) by Jaroslav Kysela < perex @ perex . cz >
2005-04-17 02:20:36 +04: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/mm.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# include <linux/file.h>
# include <linux/slab.h>
# include <linux/time.h>
2011-08-25 17:35:03 +04:00
# include <linux/pm_qos.h>
2013-05-08 03:19:08 +04:00
# include <linux/aio.h>
2009-11-26 14:40:21 +03:00
# include <linux/dma-mapping.h>
2005-04-17 02:20:36 +04:00
# include <sound/core.h>
# include <sound/control.h>
# include <sound/info.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/timer.h>
# include <sound/minors.h>
# include <asm/io.h>
2010-05-12 12:32:42 +04:00
# if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
# include <dma-coherence.h>
# endif
2005-04-17 02:20:36 +04:00
/*
* Compatibility
*/
2005-11-17 15:59:38 +03:00
struct snd_pcm_hw_params_old {
2005-04-17 02:20:36 +04:00
unsigned int flags ;
unsigned int masks [ SNDRV_PCM_HW_PARAM_SUBFORMAT -
SNDRV_PCM_HW_PARAM_ACCESS + 1 ] ;
2005-11-17 15:59:38 +03:00
struct snd_interval intervals [ SNDRV_PCM_HW_PARAM_TICK_TIME -
2005-04-17 02:20:36 +04:00
SNDRV_PCM_HW_PARAM_SAMPLE_BITS + 1 ] ;
unsigned int rmask ;
unsigned int cmask ;
unsigned int info ;
unsigned int msbits ;
unsigned int rate_num ;
unsigned int rate_den ;
2005-11-17 15:59:38 +03:00
snd_pcm_uframes_t fifo_size ;
2005-04-17 02:20:36 +04:00
unsigned char reserved [ 64 ] ;
} ;
2005-12-01 12:51:58 +03:00
# ifdef CONFIG_SND_SUPPORT_OLD_API
2005-11-17 15:59:38 +03:00
# define SNDRV_PCM_IOCTL_HW_REFINE_OLD _IOWR('A', 0x10, struct snd_pcm_hw_params_old)
# define SNDRV_PCM_IOCTL_HW_PARAMS_OLD _IOWR('A', 0x11, struct snd_pcm_hw_params_old)
2005-04-17 02:20:36 +04:00
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_refine_old_user ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params_old __user * _oparams ) ;
static int snd_pcm_hw_params_old_user ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params_old __user * _oparams ) ;
2005-12-01 12:51:58 +03:00
# endif
2005-11-20 16:06:59 +03:00
static int snd_pcm_open ( struct file * file , struct snd_pcm * pcm , int stream ) ;
2005-04-17 02:20:36 +04:00
/*
*
*/
DEFINE_RWLOCK ( snd_pcm_link_rwlock ) ;
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_link_rwlock ) ;
2005-04-17 02:20:36 +04:00
2006-04-28 17:13:40 +04:00
static DECLARE_RWSEM ( snd_pcm_link_rwsem ) ;
2005-04-17 02:20:36 +04: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 ) ;
}
2005-11-17 15:59:38 +03:00
int snd_pcm_info ( struct snd_pcm_substream * substream , struct snd_pcm_info * info )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime ;
struct snd_pcm * pcm = substream - > pcm ;
struct snd_pcm_str * pstr = substream - > pstr ;
2005-04-17 02:20:36 +04:00
memset ( info , 0 , sizeof ( * info ) ) ;
info - > card = pcm - > card - > number ;
info - > device = pcm - > device ;
info - > stream = substream - > stream ;
info - > subdevice = substream - > number ;
strlcpy ( info - > id , pcm - > id , sizeof ( info - > id ) ) ;
strlcpy ( info - > name , pcm - > name , sizeof ( info - > name ) ) ;
info - > dev_class = pcm - > dev_class ;
info - > dev_subclass = pcm - > dev_subclass ;
info - > subdevices_count = pstr - > substream_count ;
info - > subdevices_avail = pstr - > substream_count - pstr - > substream_opened ;
strlcpy ( info - > subname , substream - > name , sizeof ( info - > subname ) ) ;
runtime = substream - > runtime ;
/* AB: FIXME!!! This is definitely nonsense */
if ( runtime ) {
info - > sync = runtime - > sync ;
substream - > ops - > ioctl ( substream , SNDRV_PCM_IOCTL1_INFO , info ) ;
}
return 0 ;
}
2005-11-17 15:59:38 +03:00
int snd_pcm_info_user ( struct snd_pcm_substream * substream ,
struct snd_pcm_info __user * _info )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_info * info ;
2005-04-17 02:20:36 +04:00
int err ;
info = kmalloc ( sizeof ( * info ) , GFP_KERNEL ) ;
if ( ! info )
return - ENOMEM ;
err = snd_pcm_info ( substream , info ) ;
if ( err > = 0 ) {
if ( copy_to_user ( _info , info , sizeof ( * info ) ) )
err = - EFAULT ;
}
kfree ( info ) ;
return err ;
}
# undef RULES_DEBUG
# ifdef RULES_DEBUG
# define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v
2010-09-14 08:24:02 +04:00
static const char * const snd_pcm_hw_param_names [ ] = {
2005-04-17 02:20:36 +04:00
HW_PARAM ( ACCESS ) ,
HW_PARAM ( FORMAT ) ,
HW_PARAM ( SUBFORMAT ) ,
HW_PARAM ( SAMPLE_BITS ) ,
HW_PARAM ( FRAME_BITS ) ,
HW_PARAM ( CHANNELS ) ,
HW_PARAM ( RATE ) ,
HW_PARAM ( PERIOD_TIME ) ,
HW_PARAM ( PERIOD_SIZE ) ,
HW_PARAM ( PERIOD_BYTES ) ,
HW_PARAM ( PERIODS ) ,
HW_PARAM ( BUFFER_TIME ) ,
HW_PARAM ( BUFFER_SIZE ) ,
HW_PARAM ( BUFFER_BYTES ) ,
HW_PARAM ( TICK_TIME ) ,
} ;
# endif
2005-11-17 15:59:38 +03:00
int snd_pcm_hw_refine ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
2005-04-17 02:20:36 +04:00
{
unsigned int k ;
2005-11-17 15:59:38 +03:00
struct snd_pcm_hardware * hw ;
struct snd_interval * i = NULL ;
struct snd_mask * m = NULL ;
struct snd_pcm_hw_constraints * constrs = & substream - > runtime - > hw_constraints ;
2005-04-17 02:20:36 +04:00
unsigned int rstamps [ constrs - > rules_num ] ;
unsigned int vstamps [ SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1 ] ;
unsigned int stamp = 2 ;
int changed , again ;
params - > info = 0 ;
params - > fifo_size = 0 ;
if ( params - > rmask & ( 1 < < SNDRV_PCM_HW_PARAM_SAMPLE_BITS ) )
params - > msbits = 0 ;
if ( params - > rmask & ( 1 < < SNDRV_PCM_HW_PARAM_RATE ) ) {
params - > rate_num = 0 ;
params - > rate_den = 0 ;
}
for ( k = SNDRV_PCM_HW_PARAM_FIRST_MASK ; k < = SNDRV_PCM_HW_PARAM_LAST_MASK ; k + + ) {
m = hw_param_mask ( params , k ) ;
if ( snd_mask_empty ( m ) )
return - EINVAL ;
if ( ! ( params - > rmask & ( 1 < < k ) ) )
continue ;
# ifdef RULES_DEBUG
2009-02-05 17:51:04 +03:00
printk ( KERN_DEBUG " %s = " , snd_pcm_hw_param_names [ k ] ) ;
2005-04-17 02:20:36 +04:00
printk ( " %04x%04x%04x%04x -> " , m - > bits [ 3 ] , m - > bits [ 2 ] , m - > bits [ 1 ] , m - > bits [ 0 ] ) ;
# endif
changed = snd_mask_refine ( m , constrs_mask ( constrs , k ) ) ;
# ifdef RULES_DEBUG
printk ( " %04x%04x%04x%04x \n " , m - > bits [ 3 ] , m - > bits [ 2 ] , m - > bits [ 1 ] , m - > bits [ 0 ] ) ;
# endif
if ( changed )
params - > cmask | = 1 < < k ;
if ( changed < 0 )
return changed ;
}
for ( k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL ; k < = SNDRV_PCM_HW_PARAM_LAST_INTERVAL ; k + + ) {
i = hw_param_interval ( params , k ) ;
if ( snd_interval_empty ( i ) )
return - EINVAL ;
if ( ! ( params - > rmask & ( 1 < < k ) ) )
continue ;
# ifdef RULES_DEBUG
2009-02-05 17:51:04 +03:00
printk ( KERN_DEBUG " %s = " , snd_pcm_hw_param_names [ k ] ) ;
2005-04-17 02:20:36 +04:00
if ( i - > empty )
printk ( " empty " ) ;
else
printk ( " %c%u %u%c " ,
i - > openmin ? ' ( ' : ' [ ' , i - > min ,
i - > max , i - > openmax ? ' ) ' : ' ] ' ) ;
printk ( " -> " ) ;
# endif
changed = snd_interval_refine ( i , constrs_interval ( constrs , k ) ) ;
# ifdef RULES_DEBUG
if ( i - > empty )
printk ( " empty \n " ) ;
else
printk ( " %c%u %u%c \n " ,
i - > openmin ? ' ( ' : ' [ ' , i - > min ,
i - > max , i - > openmax ? ' ) ' : ' ] ' ) ;
# endif
if ( changed )
params - > cmask | = 1 < < k ;
if ( changed < 0 )
return changed ;
}
for ( k = 0 ; k < constrs - > rules_num ; k + + )
rstamps [ k ] = 0 ;
for ( k = 0 ; k < = SNDRV_PCM_HW_PARAM_LAST_INTERVAL ; k + + )
vstamps [ k ] = ( params - > rmask & ( 1 < < k ) ) ? 1 : 0 ;
do {
again = 0 ;
for ( k = 0 ; k < constrs - > rules_num ; k + + ) {
2005-11-17 15:59:38 +03:00
struct snd_pcm_hw_rule * r = & constrs - > rules [ k ] ;
2005-04-17 02:20:36 +04:00
unsigned int d ;
int doit = 0 ;
if ( r - > cond & & ! ( r - > cond & params - > flags ) )
continue ;
for ( d = 0 ; r - > deps [ d ] > = 0 ; d + + ) {
if ( vstamps [ r - > deps [ d ] ] > rstamps [ k ] ) {
doit = 1 ;
break ;
}
}
if ( ! doit )
continue ;
# ifdef RULES_DEBUG
2009-02-05 17:51:04 +03:00
printk ( KERN_DEBUG " Rule %d [%p]: " , k , r - > func ) ;
2005-04-17 02:20:36 +04:00
if ( r - > var > = 0 ) {
printk ( " %s = " , snd_pcm_hw_param_names [ r - > var ] ) ;
if ( hw_is_mask ( r - > var ) ) {
m = hw_param_mask ( params , r - > var ) ;
printk ( " %x " , * m - > bits ) ;
} else {
i = hw_param_interval ( params , r - > var ) ;
if ( i - > empty )
printk ( " empty " ) ;
else
printk ( " %c%u %u%c " ,
i - > openmin ? ' ( ' : ' [ ' , i - > min ,
i - > max , i - > openmax ? ' ) ' : ' ] ' ) ;
}
}
# endif
changed = r - > func ( params , r ) ;
# ifdef RULES_DEBUG
if ( r - > var > = 0 ) {
printk ( " -> " ) ;
if ( hw_is_mask ( r - > var ) )
printk ( " %x " , * m - > bits ) ;
else {
if ( i - > empty )
printk ( " empty " ) ;
else
printk ( " %c%u %u%c " ,
i - > openmin ? ' ( ' : ' [ ' , i - > min ,
i - > max , i - > openmax ? ' ) ' : ' ] ' ) ;
}
}
printk ( " \n " ) ;
# endif
rstamps [ k ] = stamp ;
if ( changed & & r - > var > = 0 ) {
params - > cmask | = ( 1 < < r - > var ) ;
vstamps [ r - > var ] = stamp ;
again = 1 ;
}
if ( changed < 0 )
return changed ;
stamp + + ;
}
} while ( again ) ;
if ( ! params - > msbits ) {
i = hw_param_interval ( params , SNDRV_PCM_HW_PARAM_SAMPLE_BITS ) ;
if ( snd_interval_single ( i ) )
params - > msbits = snd_interval_value ( i ) ;
}
if ( ! params - > rate_den ) {
i = hw_param_interval ( params , SNDRV_PCM_HW_PARAM_RATE ) ;
if ( snd_interval_single ( i ) ) {
params - > rate_num = snd_interval_value ( i ) ;
params - > rate_den = 1 ;
}
}
hw = & substream - > runtime - > hw ;
if ( ! params - > info )
2009-04-27 11:44:40 +04:00
params - > info = hw - > info & ~ SNDRV_PCM_INFO_FIFO_IN_FRAMES ;
if ( ! params - > fifo_size ) {
2010-02-16 13:55:43 +03:00
m = hw_param_mask ( params , SNDRV_PCM_HW_PARAM_FORMAT ) ;
i = hw_param_interval ( params , SNDRV_PCM_HW_PARAM_CHANNELS ) ;
if ( snd_mask_min ( m ) = = snd_mask_max ( m ) & &
snd_interval_min ( i ) = = snd_interval_max ( i ) ) {
2009-04-27 11:44:40 +04:00
changed = substream - > ops - > ioctl ( substream ,
SNDRV_PCM_IOCTL1_FIFO_SIZE , params ) ;
2009-06-21 22:26:59 +04:00
if ( changed < 0 )
2009-04-27 11:44:40 +04:00
return changed ;
}
}
2005-04-17 02:20:36 +04:00
params - > rmask = 0 ;
return 0 ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_hw_refine ) ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_refine_user ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params __user * _params )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_hw_params * params ;
2005-04-17 02:20:36 +04:00
int err ;
2009-04-10 05:43:08 +04:00
params = memdup_user ( _params , sizeof ( * params ) ) ;
if ( IS_ERR ( params ) )
return PTR_ERR ( params ) ;
2005-04-17 02:20:36 +04:00
err = snd_pcm_hw_refine ( substream , params ) ;
if ( copy_to_user ( _params , params , sizeof ( * params ) ) ) {
if ( ! err )
err = - EFAULT ;
}
2009-04-10 05:43:08 +04:00
2005-04-17 02:20:36 +04:00
kfree ( params ) ;
return err ;
}
2006-10-01 10:27:19 +04:00
static int period_to_usecs ( struct snd_pcm_runtime * runtime )
{
int usecs ;
if ( ! runtime - > rate )
return - 1 ; /* invalid */
/* take 75% of period time as the deadline */
usecs = ( 750000 / runtime - > rate ) * runtime - > period_size ;
usecs + = ( ( 750000 % runtime - > rate ) * runtime - > period_size ) /
runtime - > rate ;
return usecs ;
}
2012-10-12 17:07:34 +04:00
static void snd_pcm_set_state ( struct snd_pcm_substream * substream , int state )
{
snd_pcm_stream_lock_irq ( substream ) ;
if ( substream - > runtime - > status - > state ! = SNDRV_PCM_STATE_DISCONNECTED )
substream - > runtime - > status - > state = state ;
snd_pcm_stream_unlock_irq ( substream ) ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime ;
2006-10-01 10:27:19 +04:00
int err , usecs ;
2005-04-17 02:20:36 +04:00
unsigned int bits ;
snd_pcm_uframes_t frames ;
2008-08-08 19:09:09 +04:00
if ( PCM_RUNTIME_CHECK ( substream ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
snd_pcm_stream_lock_irq ( substream ) ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_OPEN :
case SNDRV_PCM_STATE_SETUP :
case SNDRV_PCM_STATE_PREPARED :
break ;
default :
snd_pcm_stream_unlock_irq ( substream ) ;
return - EBADFD ;
}
snd_pcm_stream_unlock_irq ( substream ) ;
# if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
if ( ! substream - > oss . oss )
# endif
2006-04-28 17:13:41 +04:00
if ( atomic_read ( & substream - > mmap_count ) )
2005-04-17 02:20:36 +04:00
return - EBADFD ;
params - > rmask = ~ 0U ;
err = snd_pcm_hw_refine ( substream , params ) ;
if ( err < 0 )
goto _error ;
err = snd_pcm_hw_params_choose ( substream , params ) ;
if ( err < 0 )
goto _error ;
if ( substream - > ops - > hw_params ! = NULL ) {
err = substream - > ops - > hw_params ( substream , params ) ;
if ( err < 0 )
goto _error ;
}
runtime - > access = params_access ( params ) ;
runtime - > format = params_format ( params ) ;
runtime - > subformat = params_subformat ( params ) ;
runtime - > channels = params_channels ( params ) ;
runtime - > rate = params_rate ( params ) ;
runtime - > period_size = params_period_size ( params ) ;
runtime - > periods = params_periods ( params ) ;
runtime - > buffer_size = params_buffer_size ( params ) ;
runtime - > info = params - > info ;
runtime - > rate_num = params - > rate_num ;
runtime - > rate_den = params - > rate_den ;
2010-11-15 12:46:23 +03:00
runtime - > no_period_wakeup =
( params - > info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP ) & &
( params - > flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP ) ;
2005-04-17 02:20:36 +04:00
bits = snd_pcm_format_physical_width ( runtime - > format ) ;
runtime - > sample_bits = bits ;
bits * = runtime - > channels ;
runtime - > frame_bits = bits ;
frames = 1 ;
while ( bits % 8 ! = 0 ) {
bits * = 2 ;
frames * = 2 ;
}
runtime - > byte_align = bits / 8 ;
runtime - > min_align = frames ;
/* Default sw params */
runtime - > tstamp_mode = SNDRV_PCM_TSTAMP_NONE ;
runtime - > period_step = 1 ;
runtime - > control - > avail_min = runtime - > period_size ;
runtime - > start_threshold = 1 ;
runtime - > stop_threshold = runtime - > buffer_size ;
runtime - > silence_threshold = 0 ;
runtime - > silence_size = 0 ;
2010-05-21 11:15:59 +04:00
runtime - > boundary = runtime - > buffer_size ;
while ( runtime - > boundary * 2 < = LONG_MAX - runtime - > buffer_size )
runtime - > boundary * = 2 ;
2005-04-17 02:20:36 +04:00
snd_pcm_timer_resolution_change ( substream ) ;
2012-10-12 17:07:34 +04:00
snd_pcm_set_state ( substream , SNDRV_PCM_STATE_SETUP ) ;
2006-10-01 10:27:19 +04:00
2010-07-06 00:53:06 +04:00
if ( pm_qos_request_active ( & substream - > latency_pm_qos_req ) )
pm_qos_remove_request ( & substream - > latency_pm_qos_req ) ;
2006-10-01 10:27:19 +04:00
if ( ( usecs = period_to_usecs ( runtime ) ) > = 0 )
2010-07-06 00:53:06 +04:00
pm_qos_add_request ( & substream - > latency_pm_qos_req ,
PM_QOS_CPU_DMA_LATENCY , usecs ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
_error :
2011-03-31 05:57:33 +04:00
/* hardware might be unusable from this time,
2005-04-17 02:20:36 +04:00
so we force application to retry to set
the correct hardware parameter settings */
2012-10-12 17:07:34 +04:00
snd_pcm_set_state ( substream , SNDRV_PCM_STATE_OPEN ) ;
2005-04-17 02:20:36 +04:00
if ( substream - > ops - > hw_free ! = NULL )
substream - > ops - > hw_free ( substream ) ;
return err ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_params_user ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params __user * _params )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_hw_params * params ;
2005-04-17 02:20:36 +04:00
int err ;
2009-04-10 05:43:08 +04:00
params = memdup_user ( _params , sizeof ( * params ) ) ;
if ( IS_ERR ( params ) )
return PTR_ERR ( params ) ;
2005-04-17 02:20:36 +04:00
err = snd_pcm_hw_params ( substream , params ) ;
if ( copy_to_user ( _params , params , sizeof ( * params ) ) ) {
if ( ! err )
err = - EFAULT ;
}
2009-04-10 05:43:08 +04:00
2005-04-17 02:20:36 +04:00
kfree ( params ) ;
return err ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_free ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime ;
2005-04-17 02:20:36 +04:00
int result = 0 ;
2008-08-08 19:09:09 +04:00
if ( PCM_RUNTIME_CHECK ( substream ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
snd_pcm_stream_lock_irq ( substream ) ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_SETUP :
case SNDRV_PCM_STATE_PREPARED :
break ;
default :
snd_pcm_stream_unlock_irq ( substream ) ;
return - EBADFD ;
}
snd_pcm_stream_unlock_irq ( substream ) ;
2006-04-28 17:13:41 +04:00
if ( atomic_read ( & substream - > mmap_count ) )
2005-04-17 02:20:36 +04:00
return - EBADFD ;
if ( substream - > ops - > hw_free )
result = substream - > ops - > hw_free ( substream ) ;
2012-10-12 17:07:34 +04:00
snd_pcm_set_state ( substream , SNDRV_PCM_STATE_OPEN ) ;
2010-07-06 00:53:06 +04:00
pm_qos_remove_request ( & substream - > latency_pm_qos_req ) ;
2005-04-17 02:20:36 +04:00
return result ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_sw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_sw_params * params )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime ;
2010-01-07 17:36:31 +03:00
int err ;
2005-04-17 02:20:36 +04:00
2008-08-08 19:09:09 +04:00
if ( PCM_RUNTIME_CHECK ( substream ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
snd_pcm_stream_lock_irq ( substream ) ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN ) {
snd_pcm_stream_unlock_irq ( substream ) ;
return - EBADFD ;
}
snd_pcm_stream_unlock_irq ( substream ) ;
if ( params - > tstamp_mode > SNDRV_PCM_TSTAMP_LAST )
return - EINVAL ;
if ( params - > avail_min = = 0 )
return - EINVAL ;
if ( params - > silence_size > = runtime - > boundary ) {
if ( params - > silence_threshold ! = 0 )
return - EINVAL ;
} else {
if ( params - > silence_size > params - > silence_threshold )
return - EINVAL ;
if ( params - > silence_threshold > runtime - > buffer_size )
return - EINVAL ;
}
2010-01-07 17:36:31 +03:00
err = 0 ;
2005-04-17 02:20:36 +04:00
snd_pcm_stream_lock_irq ( substream ) ;
runtime - > tstamp_mode = params - > tstamp_mode ;
runtime - > period_step = params - > period_step ;
runtime - > control - > avail_min = params - > avail_min ;
runtime - > start_threshold = params - > start_threshold ;
runtime - > stop_threshold = params - > stop_threshold ;
runtime - > silence_threshold = params - > silence_threshold ;
runtime - > silence_size = params - > silence_size ;
params - > boundary = runtime - > boundary ;
if ( snd_pcm_running ( substream ) ) {
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK & &
runtime - > silence_size > 0 )
snd_pcm_playback_silence ( substream , ULONG_MAX ) ;
2010-01-07 17:36:31 +03:00
err = snd_pcm_update_state ( substream , runtime ) ;
2005-04-17 02:20:36 +04:00
}
snd_pcm_stream_unlock_irq ( substream ) ;
2010-01-07 17:36:31 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_sw_params_user ( struct snd_pcm_substream * substream ,
struct snd_pcm_sw_params __user * _params )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_sw_params params ;
2005-04-17 02:20:36 +04:00
int err ;
if ( copy_from_user ( & params , _params , sizeof ( params ) ) )
return - EFAULT ;
err = snd_pcm_sw_params ( substream , & params ) ;
if ( copy_to_user ( _params , & params , sizeof ( params ) ) )
return - EFAULT ;
return err ;
}
2005-11-17 15:59:38 +03:00
int snd_pcm_status ( struct snd_pcm_substream * substream ,
struct snd_pcm_status * status )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
snd_pcm_stream_lock_irq ( substream ) ;
status - > state = runtime - > status - > state ;
status - > suspended_state = runtime - > status - > suspended_state ;
if ( status - > state = = SNDRV_PCM_STATE_OPEN )
goto _end ;
status - > trigger_tstamp = runtime - > trigger_tstamp ;
2008-01-11 10:45:08 +03:00
if ( snd_pcm_running ( substream ) ) {
2005-04-17 02:20:36 +04:00
snd_pcm_update_hw_ptr ( substream ) ;
2008-01-11 10:45:08 +03:00
if ( runtime - > tstamp_mode = = SNDRV_PCM_TSTAMP_ENABLE ) {
status - > tstamp = runtime - > status - > tstamp ;
2012-10-23 01:42:15 +04:00
status - > audio_tstamp =
runtime - > status - > audio_tstamp ;
2008-01-11 10:45:08 +03:00
goto _tstamp_end ;
}
}
2008-01-08 14:24:01 +03:00
snd_pcm_gettime ( runtime , & status - > tstamp ) ;
2008-01-11 10:45:08 +03:00
_tstamp_end :
2005-04-17 02:20:36 +04:00
status - > appl_ptr = runtime - > control - > appl_ptr ;
status - > hw_ptr = runtime - > status - > hw_ptr ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
status - > avail = snd_pcm_playback_avail ( runtime ) ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_RUNNING | |
2008-10-13 05:07:14 +04:00
runtime - > status - > state = = SNDRV_PCM_STATE_DRAINING ) {
2005-04-17 02:20:36 +04:00
status - > delay = runtime - > buffer_size - status - > avail ;
2008-10-13 05:07:14 +04:00
status - > delay + = runtime - > delay ;
} else
2005-04-17 02:20:36 +04:00
status - > delay = 0 ;
} else {
status - > avail = snd_pcm_capture_avail ( runtime ) ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_RUNNING )
2008-10-13 05:07:14 +04:00
status - > delay = status - > avail + runtime - > delay ;
2005-04-17 02:20:36 +04:00
else
status - > delay = 0 ;
}
status - > avail_max = runtime - > avail_max ;
status - > overrange = runtime - > overrange ;
runtime - > avail_max = 0 ;
runtime - > overrange = 0 ;
_end :
snd_pcm_stream_unlock_irq ( substream ) ;
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_status_user ( struct snd_pcm_substream * substream ,
struct snd_pcm_status __user * _status )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_status status ;
2005-04-17 02:20:36 +04:00
int res ;
memset ( & status , 0 , sizeof ( status ) ) ;
res = snd_pcm_status ( substream , & status ) ;
if ( res < 0 )
return res ;
if ( copy_to_user ( _status , & status , sizeof ( status ) ) )
return - EFAULT ;
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_channel_info ( struct snd_pcm_substream * substream ,
struct snd_pcm_channel_info * info )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime ;
2005-04-17 02:20:36 +04:00
unsigned int channel ;
channel = info - > channel ;
runtime = substream - > runtime ;
snd_pcm_stream_lock_irq ( substream ) ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN ) {
snd_pcm_stream_unlock_irq ( substream ) ;
return - EBADFD ;
}
snd_pcm_stream_unlock_irq ( substream ) ;
if ( channel > = runtime - > channels )
return - EINVAL ;
memset ( info , 0 , sizeof ( * info ) ) ;
info - > channel = channel ;
return substream - > ops - > ioctl ( substream , SNDRV_PCM_IOCTL1_CHANNEL_INFO , info ) ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_channel_info_user ( struct snd_pcm_substream * substream ,
struct snd_pcm_channel_info __user * _info )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_channel_info info ;
2005-04-17 02:20:36 +04:00
int res ;
if ( copy_from_user ( & info , _info , sizeof ( info ) ) )
return - EFAULT ;
res = snd_pcm_channel_info ( substream , & info ) ;
if ( res < 0 )
return res ;
if ( copy_to_user ( _info , & info , sizeof ( info ) ) )
return - EFAULT ;
return 0 ;
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_trigger_tstamp ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
if ( runtime - > trigger_master = = NULL )
return ;
if ( runtime - > trigger_master = = substream ) {
2007-12-13 12:19:42 +03:00
snd_pcm_gettime ( runtime , & runtime - > trigger_tstamp ) ;
2005-04-17 02:20:36 +04:00
} else {
snd_pcm_trigger_tstamp ( runtime - > trigger_master ) ;
runtime - > trigger_tstamp = runtime - > trigger_master - > runtime - > trigger_tstamp ;
}
runtime - > trigger_master = NULL ;
}
struct action_ops {
2005-11-17 15:59:38 +03:00
int ( * pre_action ) ( struct snd_pcm_substream * substream , int state ) ;
int ( * do_action ) ( struct snd_pcm_substream * substream , int state ) ;
void ( * undo_action ) ( struct snd_pcm_substream * substream , int state ) ;
void ( * post_action ) ( struct snd_pcm_substream * substream , int state ) ;
2005-04-17 02:20:36 +04:00
} ;
/*
* this functions is core for handling of linked stream
* Note : the stream state might be changed also on failure
* Note2 : call with calling stream lock + link lock
*/
static int snd_pcm_action_group ( struct action_ops * ops ,
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
int state , int do_lock )
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * s = NULL ;
struct snd_pcm_substream * s1 ;
2005-04-17 02:20:36 +04:00
int res = 0 ;
2007-02-22 14:52:53 +03:00
snd_pcm_group_for_each_entry ( s , substream ) {
2005-04-17 02:20:36 +04:00
if ( do_lock & & s ! = substream )
2007-04-05 18:57:41 +04:00
spin_lock_nested ( & s - > self_group . lock ,
SINGLE_DEPTH_NESTING ) ;
2005-04-17 02:20:36 +04:00
res = ops - > pre_action ( s , state ) ;
if ( res < 0 )
goto _unlock ;
}
2007-02-22 14:52:53 +03:00
snd_pcm_group_for_each_entry ( s , substream ) {
2005-04-17 02:20:36 +04:00
res = ops - > do_action ( s , state ) ;
if ( res < 0 ) {
if ( ops - > undo_action ) {
2007-02-22 14:52:53 +03:00
snd_pcm_group_for_each_entry ( s1 , substream ) {
2005-04-17 02:20:36 +04:00
if ( s1 = = s ) /* failed stream */
break ;
ops - > undo_action ( s1 , state ) ;
}
}
s = NULL ; /* unlock all */
goto _unlock ;
}
}
2007-02-22 14:52:53 +03:00
snd_pcm_group_for_each_entry ( s , substream ) {
2005-04-17 02:20:36 +04:00
ops - > post_action ( s , state ) ;
}
_unlock :
if ( do_lock ) {
/* unlock streams */
2007-02-22 14:52:53 +03:00
snd_pcm_group_for_each_entry ( s1 , substream ) {
2005-04-17 02:20:36 +04:00
if ( s1 ! = substream )
spin_unlock ( & s1 - > self_group . lock ) ;
if ( s1 = = s ) /* end */
break ;
}
}
return res ;
}
/*
* Note : call with stream lock
*/
static int snd_pcm_action_single ( struct action_ops * ops ,
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
int state )
{
int res ;
res = ops - > pre_action ( substream , state ) ;
if ( res < 0 )
return res ;
res = ops - > do_action ( substream , state ) ;
if ( res = = 0 )
ops - > post_action ( substream , state ) ;
else if ( ops - > undo_action )
ops - > undo_action ( substream , state ) ;
return res ;
}
/*
* Note : call with stream lock
*/
static int snd_pcm_action ( struct action_ops * ops ,
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
int state )
{
int res ;
if ( snd_pcm_stream_linked ( substream ) ) {
if ( ! spin_trylock ( & substream - > group - > lock ) ) {
spin_unlock ( & substream - > self_group . lock ) ;
spin_lock ( & substream - > group - > lock ) ;
spin_lock ( & substream - > self_group . lock ) ;
}
res = snd_pcm_action_group ( ops , substream , state , 1 ) ;
spin_unlock ( & substream - > group - > lock ) ;
} else {
res = snd_pcm_action_single ( ops , substream , state ) ;
}
return res ;
}
/*
* Note : don ' t use any locks before
*/
static int snd_pcm_action_lock_irq ( struct action_ops * ops ,
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
int state )
{
int res ;
read_lock_irq ( & snd_pcm_link_rwlock ) ;
if ( snd_pcm_stream_linked ( substream ) ) {
spin_lock ( & substream - > group - > lock ) ;
spin_lock ( & substream - > self_group . lock ) ;
res = snd_pcm_action_group ( ops , substream , state , 1 ) ;
spin_unlock ( & substream - > self_group . lock ) ;
spin_unlock ( & substream - > group - > lock ) ;
} else {
spin_lock ( & substream - > self_group . lock ) ;
res = snd_pcm_action_single ( ops , substream , state ) ;
spin_unlock ( & substream - > self_group . lock ) ;
}
read_unlock_irq ( & snd_pcm_link_rwlock ) ;
return res ;
}
/*
*/
static int snd_pcm_action_nonatomic ( struct action_ops * ops ,
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
int state )
{
int res ;
down_read ( & snd_pcm_link_rwsem ) ;
if ( snd_pcm_stream_linked ( substream ) )
res = snd_pcm_action_group ( ops , substream , state , 0 ) ;
else
res = snd_pcm_action_single ( ops , substream , state ) ;
up_read ( & snd_pcm_link_rwsem ) ;
return res ;
}
/*
* start callbacks
*/
2005-11-17 15:59:38 +03:00
static int snd_pcm_pre_start ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
if ( runtime - > status - > state ! = SNDRV_PCM_STATE_PREPARED )
return - EBADFD ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK & &
! snd_pcm_playback_data ( substream ) )
return - EPIPE ;
runtime - > trigger_master = substream ;
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_do_start ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
if ( substream - > runtime - > trigger_master ! = substream )
return 0 ;
return substream - > ops - > trigger ( substream , SNDRV_PCM_TRIGGER_START ) ;
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_undo_start ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
if ( substream - > runtime - > trigger_master = = substream )
substream - > ops - > trigger ( substream , SNDRV_PCM_TRIGGER_STOP ) ;
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_post_start ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
snd_pcm_trigger_tstamp ( substream ) ;
2009-05-27 12:49:26 +04:00
runtime - > hw_ptr_jiffies = jiffies ;
2010-08-18 16:16:54 +04:00
runtime - > hw_ptr_buffer_jiffies = ( runtime - > buffer_size * HZ ) /
runtime - > rate ;
2005-04-17 02:20:36 +04:00
runtime - > status - > state = state ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK & &
runtime - > silence_size > 0 )
snd_pcm_playback_silence ( substream , ULONG_MAX ) ;
if ( substream - > timer )
2005-11-17 15:59:38 +03:00
snd_timer_notify ( substream - > timer , SNDRV_TIMER_EVENT_MSTART ,
& runtime - > trigger_tstamp ) ;
2005-04-17 02:20:36 +04:00
}
static struct action_ops snd_pcm_action_start = {
. pre_action = snd_pcm_pre_start ,
. do_action = snd_pcm_do_start ,
. undo_action = snd_pcm_undo_start ,
. post_action = snd_pcm_post_start
} ;
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_start - start all linked streams
2005-09-07 15:38:19 +04:00
* @ substream : the PCM substream instance
2013-03-12 01:05:14 +04:00
*
* Return : Zero if successful , or a negative error code .
2005-04-17 02:20:36 +04:00
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_start ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
return snd_pcm_action ( & snd_pcm_action_start , substream ,
SNDRV_PCM_STATE_RUNNING ) ;
2005-04-17 02:20:36 +04:00
}
/*
* stop callbacks
*/
2005-11-17 15:59:38 +03:00
static int snd_pcm_pre_stop ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN )
return - EBADFD ;
runtime - > trigger_master = substream ;
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_do_stop ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
if ( substream - > runtime - > trigger_master = = substream & &
snd_pcm_running ( substream ) )
substream - > ops - > trigger ( substream , SNDRV_PCM_TRIGGER_STOP ) ;
return 0 ; /* unconditonally stop all substreams */
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_post_stop ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
if ( runtime - > status - > state ! = state ) {
snd_pcm_trigger_tstamp ( substream ) ;
if ( substream - > timer )
2005-11-17 15:59:38 +03:00
snd_timer_notify ( substream - > timer , SNDRV_TIMER_EVENT_MSTOP ,
& runtime - > trigger_tstamp ) ;
2005-04-17 02:20:36 +04:00
runtime - > status - > state = state ;
}
wake_up ( & runtime - > sleep ) ;
2010-01-21 12:32:15 +03:00
wake_up ( & runtime - > tsleep ) ;
2005-04-17 02:20:36 +04:00
}
static struct action_ops snd_pcm_action_stop = {
. pre_action = snd_pcm_pre_stop ,
. do_action = snd_pcm_do_stop ,
. post_action = snd_pcm_post_stop
} ;
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_stop - try to stop all running streams in the substream group
2005-09-07 15:38:19 +04:00
* @ substream : the PCM substream instance
* @ state : PCM state after stopping the stream
2005-04-17 02:20:36 +04:00
*
2008-10-16 01:38:40 +04:00
* The state of each stream is then changed to the given state unconditionally .
2013-03-12 01:05:14 +04:00
*
* Return : Zero if succesful , or a negative error code .
2005-04-17 02:20:36 +04:00
*/
2011-02-14 13:00:47 +03:00
int snd_pcm_stop ( struct snd_pcm_substream * substream , snd_pcm_state_t state )
2005-04-17 02:20:36 +04:00
{
return snd_pcm_action ( & snd_pcm_action_stop , substream , state ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_stop ) ;
2005-04-17 02:20:36 +04:00
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_drain_done - stop the DMA only when the given stream is playback
2005-09-07 15:38:19 +04:00
* @ substream : the PCM substream
2005-04-17 02:20:36 +04:00
*
2008-10-16 01:38:40 +04:00
* After stopping , the state is changed to SETUP .
2005-04-17 02:20:36 +04:00
* Unlike snd_pcm_stop ( ) , this affects only the given stream .
2013-03-12 01:05:14 +04:00
*
* Return : Zero if succesful , or a negative error code .
2005-04-17 02:20:36 +04:00
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_drain_done ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
return snd_pcm_action_single ( & snd_pcm_action_stop , substream ,
SNDRV_PCM_STATE_SETUP ) ;
2005-04-17 02:20:36 +04:00
}
/*
* pause callbacks
*/
2005-11-17 15:59:38 +03:00
static int snd_pcm_pre_pause ( struct snd_pcm_substream * substream , int push )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
if ( ! ( runtime - > info & SNDRV_PCM_INFO_PAUSE ) )
return - ENOSYS ;
if ( push ) {
if ( runtime - > status - > state ! = SNDRV_PCM_STATE_RUNNING )
return - EBADFD ;
} else if ( runtime - > status - > state ! = SNDRV_PCM_STATE_PAUSED )
return - EBADFD ;
runtime - > trigger_master = substream ;
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_do_pause ( struct snd_pcm_substream * substream , int push )
2005-04-17 02:20:36 +04:00
{
if ( substream - > runtime - > trigger_master ! = substream )
return 0 ;
2010-08-18 16:08:17 +04:00
/* some drivers might use hw_ptr to recover from the pause -
update the hw_ptr now */
if ( push )
snd_pcm_update_hw_ptr ( substream ) ;
2009-05-27 12:49:26 +04:00
/* The jiffies check in snd_pcm_update_hw_ptr*() is done by
tree-wide: fix comment/printk typos
"gadget", "through", "command", "maintain", "maintain", "controller", "address",
"between", "initiali[zs]e", "instead", "function", "select", "already",
"equal", "access", "management", "hierarchy", "registration", "interest",
"relative", "memory", "offset", "already",
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-11-01 22:38:34 +03:00
* a delta between the current jiffies , this gives a large enough
2009-05-27 12:49:26 +04:00
* delta , effectively to skip the check once .
*/
substream - > runtime - > hw_ptr_jiffies = jiffies - HZ * 1000 ;
2005-04-17 02:20:36 +04:00
return substream - > ops - > trigger ( substream ,
push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH :
SNDRV_PCM_TRIGGER_PAUSE_RELEASE ) ;
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_undo_pause ( struct snd_pcm_substream * substream , int push )
2005-04-17 02:20:36 +04:00
{
if ( substream - > runtime - > trigger_master = = substream )
substream - > ops - > trigger ( substream ,
push ? SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
SNDRV_PCM_TRIGGER_PAUSE_PUSH ) ;
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_post_pause ( struct snd_pcm_substream * substream , int push )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
snd_pcm_trigger_tstamp ( substream ) ;
if ( push ) {
runtime - > status - > state = SNDRV_PCM_STATE_PAUSED ;
if ( substream - > timer )
2005-11-17 15:59:38 +03:00
snd_timer_notify ( substream - > timer ,
SNDRV_TIMER_EVENT_MPAUSE ,
& runtime - > trigger_tstamp ) ;
2005-04-17 02:20:36 +04:00
wake_up ( & runtime - > sleep ) ;
2010-01-21 12:32:15 +03:00
wake_up ( & runtime - > tsleep ) ;
2005-04-17 02:20:36 +04:00
} else {
runtime - > status - > state = SNDRV_PCM_STATE_RUNNING ;
if ( substream - > timer )
2005-11-17 15:59:38 +03:00
snd_timer_notify ( substream - > timer ,
SNDRV_TIMER_EVENT_MCONTINUE ,
& runtime - > trigger_tstamp ) ;
2005-04-17 02:20:36 +04:00
}
}
static struct action_ops snd_pcm_action_pause = {
. pre_action = snd_pcm_pre_pause ,
. do_action = snd_pcm_do_pause ,
. undo_action = snd_pcm_undo_pause ,
. post_action = snd_pcm_post_pause
} ;
/*
* Push / release the pause for all linked streams .
*/
2005-11-17 15:59:38 +03:00
static int snd_pcm_pause ( struct snd_pcm_substream * substream , int push )
2005-04-17 02:20:36 +04:00
{
return snd_pcm_action ( & snd_pcm_action_pause , substream , push ) ;
}
# ifdef CONFIG_PM
/* suspend */
2005-11-17 15:59:38 +03:00
static int snd_pcm_pre_suspend ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
if ( runtime - > status - > state = = SNDRV_PCM_STATE_SUSPENDED )
return - EBUSY ;
runtime - > trigger_master = substream ;
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_do_suspend ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
if ( runtime - > trigger_master ! = substream )
return 0 ;
if ( ! snd_pcm_running ( substream ) )
return 0 ;
substream - > ops - > trigger ( substream , SNDRV_PCM_TRIGGER_SUSPEND ) ;
return 0 ; /* suspend unconditionally */
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_post_suspend ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
snd_pcm_trigger_tstamp ( substream ) ;
if ( substream - > timer )
2005-11-17 15:59:38 +03:00
snd_timer_notify ( substream - > timer , SNDRV_TIMER_EVENT_MSUSPEND ,
& runtime - > trigger_tstamp ) ;
2005-04-17 02:20:36 +04:00
runtime - > status - > suspended_state = runtime - > status - > state ;
runtime - > status - > state = SNDRV_PCM_STATE_SUSPENDED ;
wake_up ( & runtime - > sleep ) ;
2010-01-21 12:32:15 +03:00
wake_up ( & runtime - > tsleep ) ;
2005-04-17 02:20:36 +04:00
}
static struct action_ops snd_pcm_action_suspend = {
. pre_action = snd_pcm_pre_suspend ,
. do_action = snd_pcm_do_suspend ,
. post_action = snd_pcm_post_suspend
} ;
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_suspend - trigger SUSPEND to all linked streams
2005-09-07 15:38:19 +04:00
* @ substream : the PCM substream
2005-04-17 02:20:36 +04:00
*
* After this call , all streams are changed to SUSPENDED state .
2013-03-12 01:05:14 +04:00
*
* Return : Zero if successful ( or @ substream is % NULL ) , or a negative error
* code .
2005-04-17 02:20:36 +04:00
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_suspend ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
int err ;
unsigned long flags ;
2005-11-17 17:59:14 +03:00
if ( ! substream )
return 0 ;
2005-04-17 02:20:36 +04:00
snd_pcm_stream_lock_irqsave ( substream , flags ) ;
err = snd_pcm_action ( & snd_pcm_action_suspend , substream , 0 ) ;
snd_pcm_stream_unlock_irqrestore ( substream , flags ) ;
return err ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_suspend ) ;
2005-04-17 02:20:36 +04:00
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_suspend_all - trigger SUSPEND to all substreams in the given pcm
2005-09-07 15:38:19 +04:00
* @ pcm : the PCM instance
2005-04-17 02:20:36 +04:00
*
* After this call , all streams are changed to SUSPENDED state .
2013-03-12 01:05:14 +04:00
*
* Return : Zero if successful ( or @ pcm is % NULL ) , or a negative error code .
2005-04-17 02:20:36 +04:00
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_suspend_all ( struct snd_pcm * pcm )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream ;
2005-04-17 02:20:36 +04:00
int stream , err = 0 ;
2005-11-17 17:59:14 +03:00
if ( ! pcm )
return 0 ;
2005-04-17 02:20:36 +04:00
for ( stream = 0 ; stream < 2 ; stream + + ) {
2005-11-17 15:59:38 +03:00
for ( substream = pcm - > streams [ stream ] . substream ;
substream ; substream = substream - > next ) {
2005-04-17 02:20:36 +04:00
/* FIXME: the open/close code should lock this as well */
if ( substream - > runtime = = NULL )
continue ;
err = snd_pcm_suspend ( substream ) ;
if ( err < 0 & & err ! = - EBUSY )
return err ;
}
}
return 0 ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_suspend_all ) ;
2005-04-17 02:20:36 +04:00
/* resume */
2005-11-17 15:59:38 +03:00
static int snd_pcm_pre_resume ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
if ( ! ( runtime - > info & SNDRV_PCM_INFO_RESUME ) )
return - ENOSYS ;
runtime - > trigger_master = substream ;
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_do_resume ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
if ( runtime - > trigger_master ! = substream )
return 0 ;
/* DMA not running previously? */
if ( runtime - > status - > suspended_state ! = SNDRV_PCM_STATE_RUNNING & &
( runtime - > status - > suspended_state ! = SNDRV_PCM_STATE_DRAINING | |
substream - > stream ! = SNDRV_PCM_STREAM_PLAYBACK ) )
return 0 ;
return substream - > ops - > trigger ( substream , SNDRV_PCM_TRIGGER_RESUME ) ;
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_undo_resume ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
if ( substream - > runtime - > trigger_master = = substream & &
snd_pcm_running ( substream ) )
substream - > ops - > trigger ( substream , SNDRV_PCM_TRIGGER_SUSPEND ) ;
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_post_resume ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
snd_pcm_trigger_tstamp ( substream ) ;
if ( substream - > timer )
2005-11-17 15:59:38 +03:00
snd_timer_notify ( substream - > timer , SNDRV_TIMER_EVENT_MRESUME ,
& runtime - > trigger_tstamp ) ;
2005-04-17 02:20:36 +04:00
runtime - > status - > state = runtime - > status - > suspended_state ;
}
static struct action_ops snd_pcm_action_resume = {
. pre_action = snd_pcm_pre_resume ,
. do_action = snd_pcm_do_resume ,
. undo_action = snd_pcm_undo_resume ,
. post_action = snd_pcm_post_resume
} ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_resume ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_card * card = substream - > pcm - > card ;
2005-04-17 02:20:36 +04:00
int res ;
snd_power_lock ( card ) ;
2006-03-27 14:38:07 +04:00
if ( ( res = snd_power_wait ( card , SNDRV_CTL_POWER_D0 ) ) > = 0 )
2005-04-17 02:20:36 +04:00
res = snd_pcm_action_lock_irq ( & snd_pcm_action_resume , substream , 0 ) ;
snd_power_unlock ( card ) ;
return res ;
}
# else
2005-11-17 15:59:38 +03:00
static int snd_pcm_resume ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
return - ENOSYS ;
}
# endif /* CONFIG_PM */
/*
* xrun ioctl
*
* Change the RUNNING stream ( s ) to XRUN state .
*/
2005-11-17 15:59:38 +03:00
static int snd_pcm_xrun ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_card * card = substream - > pcm - > card ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
int result ;
snd_power_lock ( card ) ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_SUSPENDED ) {
2006-03-27 14:38:07 +04:00
result = snd_power_wait ( card , SNDRV_CTL_POWER_D0 ) ;
2005-04-17 02:20:36 +04:00
if ( result < 0 )
goto _unlock ;
}
snd_pcm_stream_lock_irq ( substream ) ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_XRUN :
result = 0 ; /* already there */
break ;
case SNDRV_PCM_STATE_RUNNING :
result = snd_pcm_stop ( substream , SNDRV_PCM_STATE_XRUN ) ;
break ;
default :
result = - EBADFD ;
}
snd_pcm_stream_unlock_irq ( substream ) ;
_unlock :
snd_power_unlock ( card ) ;
return result ;
}
/*
* reset ioctl
*/
2005-11-17 15:59:38 +03:00
static int snd_pcm_pre_reset ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_RUNNING :
case SNDRV_PCM_STATE_PREPARED :
case SNDRV_PCM_STATE_PAUSED :
case SNDRV_PCM_STATE_SUSPENDED :
return 0 ;
default :
return - EBADFD ;
}
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_do_reset ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
int err = substream - > ops - > ioctl ( substream , SNDRV_PCM_IOCTL1_RESET , NULL ) ;
if ( err < 0 )
return err ;
runtime - > hw_ptr_base = 0 ;
2010-01-26 19:08:24 +03:00
runtime - > hw_ptr_interrupt = runtime - > status - > hw_ptr -
runtime - > status - > hw_ptr % runtime - > period_size ;
2005-04-17 02:20:36 +04:00
runtime - > silence_start = runtime - > status - > hw_ptr ;
runtime - > silence_filled = 0 ;
return 0 ;
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_post_reset ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
runtime - > control - > appl_ptr = runtime - > status - > hw_ptr ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK & &
runtime - > silence_size > 0 )
snd_pcm_playback_silence ( substream , ULONG_MAX ) ;
}
static struct action_ops snd_pcm_action_reset = {
. pre_action = snd_pcm_pre_reset ,
. do_action = snd_pcm_do_reset ,
. post_action = snd_pcm_post_reset
} ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_reset ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
return snd_pcm_action_nonatomic ( & snd_pcm_action_reset , substream , 0 ) ;
}
/*
* prepare ioctl
*/
2006-04-28 17:13:41 +04:00
/* we use the second argument for updating f_flags */
static int snd_pcm_pre_prepare ( struct snd_pcm_substream * substream ,
int f_flags )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2006-11-08 17:41:29 +03:00
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN | |
runtime - > status - > state = = SNDRV_PCM_STATE_DISCONNECTED )
2005-04-17 02:20:36 +04:00
return - EBADFD ;
if ( snd_pcm_running ( substream ) )
return - EBUSY ;
2006-04-28 17:13:41 +04:00
substream - > f_flags = f_flags ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_do_prepare ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
int err ;
err = substream - > ops - > prepare ( substream ) ;
if ( err < 0 )
return err ;
return snd_pcm_do_reset ( substream , 0 ) ;
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_post_prepare ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
runtime - > control - > appl_ptr = runtime - > status - > hw_ptr ;
2012-10-12 17:07:34 +04:00
snd_pcm_set_state ( substream , SNDRV_PCM_STATE_PREPARED ) ;
2005-04-17 02:20:36 +04:00
}
static struct action_ops snd_pcm_action_prepare = {
. pre_action = snd_pcm_pre_prepare ,
. do_action = snd_pcm_do_prepare ,
. post_action = snd_pcm_post_prepare
} ;
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_prepare - prepare the PCM substream to be triggerable
2005-09-07 15:38:19 +04:00
* @ substream : the PCM substream instance
2006-04-28 17:13:41 +04:00
* @ file : file to refer f_flags
2013-03-12 01:05:14 +04:00
*
* Return : Zero if successful , or a negative error code .
2005-04-17 02:20:36 +04:00
*/
2006-04-28 17:13:41 +04:00
static int snd_pcm_prepare ( struct snd_pcm_substream * substream ,
struct file * file )
2005-04-17 02:20:36 +04:00
{
int res ;
2005-11-17 15:59:38 +03:00
struct snd_card * card = substream - > pcm - > card ;
2006-04-28 17:13:41 +04:00
int f_flags ;
if ( file )
f_flags = file - > f_flags ;
else
f_flags = substream - > f_flags ;
2005-04-17 02:20:36 +04:00
snd_power_lock ( card ) ;
2006-03-27 14:38:07 +04:00
if ( ( res = snd_power_wait ( card , SNDRV_CTL_POWER_D0 ) ) > = 0 )
2006-04-28 17:13:41 +04:00
res = snd_pcm_action_nonatomic ( & snd_pcm_action_prepare ,
substream , f_flags ) ;
2005-04-17 02:20:36 +04:00
snd_power_unlock ( card ) ;
return res ;
}
/*
* drain ioctl
*/
2005-11-17 15:59:38 +03:00
static int snd_pcm_pre_drain_init ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
2012-05-21 13:59:57 +04:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_OPEN :
case SNDRV_PCM_STATE_DISCONNECTED :
case SNDRV_PCM_STATE_SUSPENDED :
return - EBADFD ;
}
runtime - > trigger_master = substream ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_do_drain_init ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_PREPARED :
/* start playback stream if possible */
if ( ! snd_pcm_playback_empty ( substream ) ) {
snd_pcm_do_start ( substream , SNDRV_PCM_STATE_DRAINING ) ;
snd_pcm_post_start ( substream , SNDRV_PCM_STATE_DRAINING ) ;
}
break ;
case SNDRV_PCM_STATE_RUNNING :
runtime - > status - > state = SNDRV_PCM_STATE_DRAINING ;
break ;
2012-05-21 13:59:57 +04:00
case SNDRV_PCM_STATE_XRUN :
runtime - > status - > state = SNDRV_PCM_STATE_SETUP ;
break ;
2005-04-17 02:20:36 +04:00
default :
break ;
}
} else {
/* stop running stream */
if ( runtime - > status - > state = = SNDRV_PCM_STATE_RUNNING ) {
2007-12-14 14:50:16 +03:00
int new_state = snd_pcm_capture_avail ( runtime ) > 0 ?
2005-04-17 02:20:36 +04:00
SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP ;
2007-12-14 14:50:16 +03:00
snd_pcm_do_stop ( substream , new_state ) ;
snd_pcm_post_stop ( substream , new_state ) ;
2005-04-17 02:20:36 +04:00
}
}
return 0 ;
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_post_drain_init ( struct snd_pcm_substream * substream , int state )
2005-04-17 02:20:36 +04:00
{
}
static struct action_ops snd_pcm_action_drain_init = {
. pre_action = snd_pcm_pre_drain_init ,
. do_action = snd_pcm_do_drain_init ,
. post_action = snd_pcm_post_drain_init
} ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_drop ( struct snd_pcm_substream * substream ) ;
2005-04-17 02:20:36 +04:00
/*
* Drain the stream ( s ) .
* When the substream is linked , sync until the draining of all playback streams
* is finished .
* After this call , all streams are supposed to be either SETUP or DRAINING
* ( capture only ) state .
*/
2009-08-20 18:40:16 +04:00
static int snd_pcm_drain ( struct snd_pcm_substream * substream ,
struct file * file )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_card * card ;
struct snd_pcm_runtime * runtime ;
2007-02-22 14:52:53 +03:00
struct snd_pcm_substream * s ;
2009-09-17 20:46:26 +04:00
wait_queue_t wait ;
2005-04-17 02:20:36 +04:00
int result = 0 ;
2009-08-20 18:40:16 +04:00
int nonblock = 0 ;
2005-04-17 02:20:36 +04:00
card = substream - > pcm - > card ;
runtime = substream - > runtime ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN )
return - EBADFD ;
snd_power_lock ( card ) ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_SUSPENDED ) {
2006-03-27 14:38:07 +04:00
result = snd_power_wait ( card , SNDRV_CTL_POWER_D0 ) ;
2005-05-31 16:35:31 +04:00
if ( result < 0 ) {
snd_power_unlock ( card ) ;
return result ;
}
2005-04-17 02:20:36 +04:00
}
2009-08-20 18:40:16 +04:00
if ( file ) {
if ( file - > f_flags & O_NONBLOCK )
nonblock = 1 ;
} else if ( substream - > f_flags & O_NONBLOCK )
nonblock = 1 ;
2005-05-31 16:35:31 +04:00
down_read ( & snd_pcm_link_rwsem ) ;
snd_pcm_stream_lock_irq ( substream ) ;
/* resume pause */
2009-09-17 20:46:26 +04:00
if ( runtime - > status - > state = = SNDRV_PCM_STATE_PAUSED )
2005-05-31 16:35:31 +04:00
snd_pcm_pause ( substream , 0 ) ;
/* pre-start/stop - all running streams are changed to DRAINING state */
result = snd_pcm_action ( & snd_pcm_action_drain_init , substream , 0 ) ;
2009-08-20 18:40:16 +04:00
if ( result < 0 )
goto unlock ;
/* in non-blocking, we don't wait in ioctl but let caller poll */
if ( nonblock ) {
result = - EAGAIN ;
goto unlock ;
2005-05-31 16:35:31 +04:00
}
2005-04-17 02:20:36 +04:00
for ( ; ; ) {
long tout ;
2009-09-17 20:46:26 +04:00
struct snd_pcm_runtime * to_check ;
2005-04-17 02:20:36 +04:00
if ( signal_pending ( current ) ) {
result = - ERESTARTSYS ;
break ;
}
2009-09-17 20:46:26 +04:00
/* find a substream to drain */
to_check = NULL ;
snd_pcm_group_for_each_entry ( s , substream ) {
if ( s - > stream ! = SNDRV_PCM_STREAM_PLAYBACK )
continue ;
runtime = s - > runtime ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_DRAINING ) {
to_check = runtime ;
2005-05-31 16:35:31 +04:00
break ;
2009-09-17 20:46:26 +04:00
}
2005-05-31 16:35:31 +04:00
}
2009-09-17 20:46:26 +04:00
if ( ! to_check )
break ; /* all drained */
init_waitqueue_entry ( & wait , current ) ;
add_wait_queue ( & to_check - > sleep , & wait ) ;
2005-04-17 02:20:36 +04:00
snd_pcm_stream_unlock_irq ( substream ) ;
2009-09-17 20:46:26 +04:00
up_read ( & snd_pcm_link_rwsem ) ;
2005-04-17 02:20:36 +04:00
snd_power_unlock ( card ) ;
2011-05-26 10:09:38 +04:00
if ( runtime - > no_period_wakeup )
tout = MAX_SCHEDULE_TIMEOUT ;
else {
tout = 10 ;
if ( runtime - > rate ) {
long t = runtime - > period_size * 2 / runtime - > rate ;
tout = max ( t , tout ) ;
}
tout = msecs_to_jiffies ( tout * 1000 ) ;
}
tout = schedule_timeout_interruptible ( tout ) ;
2005-04-17 02:20:36 +04:00
snd_power_lock ( card ) ;
2009-09-17 20:46:26 +04:00
down_read ( & snd_pcm_link_rwsem ) ;
2005-04-17 02:20:36 +04:00
snd_pcm_stream_lock_irq ( substream ) ;
2009-09-17 20:46:26 +04:00
remove_wait_queue ( & to_check - > sleep , & wait ) ;
2012-10-16 18:43:39 +04:00
if ( card - > shutdown ) {
result = - ENODEV ;
break ;
}
2005-04-17 02:20:36 +04:00
if ( tout = = 0 ) {
if ( substream - > runtime - > status - > state = = SNDRV_PCM_STATE_SUSPENDED )
result = - ESTRPIPE ;
else {
snd_printd ( " playback drain error (DMA or IRQ trouble?) \n " ) ;
snd_pcm_stop ( substream , SNDRV_PCM_STATE_SETUP ) ;
result = - EIO ;
}
break ;
}
}
2005-05-31 16:35:31 +04:00
2009-08-20 18:40:16 +04:00
unlock :
2005-05-31 16:35:31 +04:00
snd_pcm_stream_unlock_irq ( substream ) ;
2009-09-17 20:46:26 +04:00
up_read ( & snd_pcm_link_rwsem ) ;
2005-04-17 02:20:36 +04:00
snd_power_unlock ( card ) ;
return result ;
}
/*
* drop ioctl
*
* Immediately put all linked substreams into SETUP state .
*/
2005-11-17 15:59:38 +03:00
static int snd_pcm_drop ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime ;
2005-04-17 02:20:36 +04:00
int result = 0 ;
2008-08-08 19:09:09 +04:00
if ( PCM_RUNTIME_CHECK ( substream ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
2006-11-08 17:41:29 +03:00
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN | |
2008-09-25 19:51:11 +04:00
runtime - > status - > state = = SNDRV_PCM_STATE_DISCONNECTED | |
runtime - > status - > state = = SNDRV_PCM_STATE_SUSPENDED )
2005-04-17 02:20:36 +04:00
return - EBADFD ;
snd_pcm_stream_lock_irq ( substream ) ;
/* resume pause */
if ( runtime - > status - > state = = SNDRV_PCM_STATE_PAUSED )
snd_pcm_pause ( substream , 0 ) ;
snd_pcm_stop ( substream , SNDRV_PCM_STATE_SETUP ) ;
/* runtime->control->appl_ptr = runtime->status->hw_ptr; */
snd_pcm_stream_unlock_irq ( substream ) ;
2008-09-25 19:51:11 +04:00
2005-04-17 02:20:36 +04:00
return result ;
}
2013-06-05 22:09:55 +04:00
static bool is_pcm_file ( struct file * file )
2005-04-17 02:20:36 +04:00
{
2013-06-05 22:09:55 +04:00
struct inode * inode = file_inode ( file ) ;
2005-11-20 16:06:59 +03:00
unsigned int minor ;
2013-06-05 22:09:55 +04:00
if ( ! S_ISCHR ( inode - > i_mode ) | | imajor ( inode ) ! = snd_major )
return false ;
2005-04-17 02:20:36 +04:00
minor = iminor ( inode ) ;
2013-06-05 22:09:55 +04:00
return snd_lookup_minor_data ( minor , SNDRV_DEVICE_TYPE_PCM_PLAYBACK ) | |
snd_lookup_minor_data ( minor , SNDRV_DEVICE_TYPE_PCM_CAPTURE ) ;
2005-04-17 02:20:36 +04:00
}
/*
* PCM link handling
*/
2005-11-17 15:59:38 +03:00
static int snd_pcm_link ( struct snd_pcm_substream * substream , int fd )
2005-04-17 02:20:36 +04:00
{
int res = 0 ;
2005-11-17 15:59:38 +03:00
struct snd_pcm_file * pcm_file ;
struct snd_pcm_substream * substream1 ;
2012-03-13 18:55:43 +04:00
struct snd_pcm_group * group ;
2013-06-05 22:09:55 +04:00
struct fd f = fdget ( fd ) ;
2005-04-17 02:20:36 +04:00
2013-06-05 22:09:55 +04:00
if ( ! f . file )
2005-04-17 02:20:36 +04:00
return - EBADFD ;
2013-06-05 22:09:55 +04:00
if ( ! is_pcm_file ( f . file ) ) {
res = - EBADFD ;
goto _badf ;
}
pcm_file = f . file - > private_data ;
2005-04-17 02:20:36 +04:00
substream1 = pcm_file - > substream ;
2012-03-13 18:55:43 +04:00
group = kmalloc ( sizeof ( * group ) , GFP_KERNEL ) ;
if ( ! group ) {
res = - ENOMEM ;
goto _nolock ;
}
2005-04-17 02:20:36 +04:00
down_write ( & snd_pcm_link_rwsem ) ;
write_lock_irq ( & snd_pcm_link_rwlock ) ;
if ( substream - > runtime - > status - > state = = SNDRV_PCM_STATE_OPEN | |
substream - > runtime - > status - > state ! = substream1 - > runtime - > status - > state ) {
res = - EBADFD ;
goto _end ;
}
if ( snd_pcm_stream_linked ( substream1 ) ) {
res = - EALREADY ;
goto _end ;
}
if ( ! snd_pcm_stream_linked ( substream ) ) {
2012-03-13 18:55:43 +04:00
substream - > group = group ;
2013-06-05 22:07:08 +04:00
group = NULL ;
2005-04-17 02:20:36 +04:00
spin_lock_init ( & substream - > group - > lock ) ;
INIT_LIST_HEAD ( & substream - > group - > substreams ) ;
list_add_tail ( & substream - > link_list , & substream - > group - > substreams ) ;
substream - > group - > count = 1 ;
}
list_add_tail ( & substream1 - > link_list , & substream - > group - > substreams ) ;
substream - > group - > count + + ;
substream1 - > group = substream - > group ;
_end :
write_unlock_irq ( & snd_pcm_link_rwlock ) ;
up_write ( & snd_pcm_link_rwsem ) ;
2012-03-13 18:55:43 +04:00
_nolock :
2012-10-16 15:05:59 +04:00
snd_card_unref ( substream1 - > pcm - > card ) ;
2013-06-05 22:07:08 +04:00
kfree ( group ) ;
2013-06-05 22:09:55 +04:00
_badf :
fdput ( f ) ;
2005-04-17 02:20:36 +04:00
return res ;
}
2005-11-17 15:59:38 +03:00
static void relink_to_local ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
substream - > group = & substream - > self_group ;
INIT_LIST_HEAD ( & substream - > self_group . substreams ) ;
list_add_tail ( & substream - > link_list , & substream - > self_group . substreams ) ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_unlink ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2007-02-22 14:52:53 +03:00
struct snd_pcm_substream * s ;
2005-04-17 02:20:36 +04:00
int res = 0 ;
down_write ( & snd_pcm_link_rwsem ) ;
write_lock_irq ( & snd_pcm_link_rwlock ) ;
if ( ! snd_pcm_stream_linked ( substream ) ) {
res = - EALREADY ;
goto _end ;
}
list_del ( & substream - > link_list ) ;
substream - > group - > count - - ;
if ( substream - > group - > count = = 1 ) { /* detach the last stream, too */
2007-02-22 14:52:53 +03:00
snd_pcm_group_for_each_entry ( s , substream ) {
relink_to_local ( s ) ;
2005-04-17 02:20:36 +04:00
break ;
}
kfree ( substream - > group ) ;
}
relink_to_local ( substream ) ;
_end :
write_unlock_irq ( & snd_pcm_link_rwlock ) ;
up_write ( & snd_pcm_link_rwsem ) ;
return res ;
}
/*
* hw configurator
*/
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_rule_mul ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_interval t ;
2005-04-17 02:20:36 +04:00
snd_interval_mul ( hw_param_interval_c ( params , rule - > deps [ 0 ] ) ,
hw_param_interval_c ( params , rule - > deps [ 1 ] ) , & t ) ;
return snd_interval_refine ( hw_param_interval ( params , rule - > var ) , & t ) ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_rule_div ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_interval t ;
2005-04-17 02:20:36 +04:00
snd_interval_div ( hw_param_interval_c ( params , rule - > deps [ 0 ] ) ,
hw_param_interval_c ( params , rule - > deps [ 1 ] ) , & t ) ;
return snd_interval_refine ( hw_param_interval ( params , rule - > var ) , & t ) ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_rule_muldivk ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_interval t ;
2005-04-17 02:20:36 +04:00
snd_interval_muldivk ( hw_param_interval_c ( params , rule - > deps [ 0 ] ) ,
hw_param_interval_c ( params , rule - > deps [ 1 ] ) ,
( unsigned long ) rule - > private , & t ) ;
return snd_interval_refine ( hw_param_interval ( params , rule - > var ) , & t ) ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_rule_mulkdiv ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_interval t ;
2005-04-17 02:20:36 +04:00
snd_interval_mulkdiv ( hw_param_interval_c ( params , rule - > deps [ 0 ] ) ,
( unsigned long ) rule - > private ,
hw_param_interval_c ( params , rule - > deps [ 1 ] ) , & t ) ;
return snd_interval_refine ( hw_param_interval ( params , rule - > var ) , & t ) ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_rule_format ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
2005-04-17 02:20:36 +04:00
{
unsigned int k ;
2005-11-17 15:59:38 +03:00
struct snd_interval * i = hw_param_interval ( params , rule - > deps [ 0 ] ) ;
struct snd_mask m ;
struct snd_mask * mask = hw_param_mask ( params , SNDRV_PCM_HW_PARAM_FORMAT ) ;
2005-04-17 02:20:36 +04:00
snd_mask_any ( & m ) ;
for ( k = 0 ; k < = SNDRV_PCM_FORMAT_LAST ; + + k ) {
int bits ;
if ( ! snd_mask_test ( mask , k ) )
continue ;
bits = snd_pcm_format_physical_width ( k ) ;
if ( bits < = 0 )
continue ; /* ignore invalid formats */
if ( ( unsigned ) bits < i - > min | | ( unsigned ) bits > i - > max )
snd_mask_reset ( & m , k ) ;
}
return snd_mask_refine ( mask , & m ) ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_rule_sample_bits ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_interval t ;
2005-04-17 02:20:36 +04:00
unsigned int k ;
t . min = UINT_MAX ;
t . max = 0 ;
t . openmin = 0 ;
t . openmax = 0 ;
for ( k = 0 ; k < = SNDRV_PCM_FORMAT_LAST ; + + k ) {
int bits ;
if ( ! snd_mask_test ( hw_param_mask ( params , SNDRV_PCM_HW_PARAM_FORMAT ) , k ) )
continue ;
bits = snd_pcm_format_physical_width ( k ) ;
if ( bits < = 0 )
continue ; /* ignore invalid formats */
if ( t . min > ( unsigned ) bits )
t . min = bits ;
if ( t . max < ( unsigned ) bits )
t . max = bits ;
}
t . integer = 1 ;
return snd_interval_refine ( hw_param_interval ( params , rule - > var ) , & t ) ;
}
# if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12
# error "Change this table"
# endif
static unsigned int rates [ ] = { 5512 , 8000 , 11025 , 16000 , 22050 , 32000 , 44100 ,
48000 , 64000 , 88200 , 96000 , 176400 , 192000 } ;
2007-08-13 19:38:54 +04:00
const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {
. count = ARRAY_SIZE ( rates ) ,
. list = rates ,
} ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_rule_rate ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_hardware * hw = rule - > private ;
2005-04-17 02:20:36 +04:00
return snd_interval_list ( hw_param_interval ( params , rule - > var ) ,
2007-08-13 19:38:54 +04:00
snd_pcm_known_rates . count ,
snd_pcm_known_rates . list , hw - > rates ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_rule_buffer_bytes_max ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_interval t ;
struct snd_pcm_substream * substream = rule - > private ;
2005-04-17 02:20:36 +04:00
t . min = 0 ;
t . max = substream - > buffer_bytes_max ;
t . openmin = 0 ;
t . openmax = 0 ;
t . integer = 1 ;
return snd_interval_refine ( hw_param_interval ( params , rule - > var ) , & t ) ;
}
2005-11-17 15:59:38 +03:00
int snd_pcm_hw_constraints_init ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct snd_pcm_hw_constraints * constrs = & runtime - > hw_constraints ;
2005-04-17 02:20:36 +04:00
int k , err ;
for ( k = SNDRV_PCM_HW_PARAM_FIRST_MASK ; k < = SNDRV_PCM_HW_PARAM_LAST_MASK ; k + + ) {
snd_mask_any ( constrs_mask ( constrs , k ) ) ;
}
for ( k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL ; k < = SNDRV_PCM_HW_PARAM_LAST_INTERVAL ; k + + ) {
snd_interval_any ( constrs_interval ( constrs , k ) ) ;
}
snd_interval_setinteger ( constrs_interval ( constrs , SNDRV_PCM_HW_PARAM_CHANNELS ) ) ;
snd_interval_setinteger ( constrs_interval ( constrs , SNDRV_PCM_HW_PARAM_BUFFER_SIZE ) ) ;
snd_interval_setinteger ( constrs_interval ( constrs , SNDRV_PCM_HW_PARAM_BUFFER_BYTES ) ) ;
snd_interval_setinteger ( constrs_interval ( constrs , SNDRV_PCM_HW_PARAM_SAMPLE_BITS ) ) ;
snd_interval_setinteger ( constrs_interval ( constrs , SNDRV_PCM_HW_PARAM_FRAME_BITS ) ) ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_FORMAT ,
snd_pcm_hw_rule_format , NULL ,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_SAMPLE_BITS ,
snd_pcm_hw_rule_sample_bits , NULL ,
SNDRV_PCM_HW_PARAM_FORMAT ,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_SAMPLE_BITS ,
snd_pcm_hw_rule_div , NULL ,
SNDRV_PCM_HW_PARAM_FRAME_BITS , SNDRV_PCM_HW_PARAM_CHANNELS , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_FRAME_BITS ,
snd_pcm_hw_rule_mul , NULL ,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS , SNDRV_PCM_HW_PARAM_CHANNELS , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_FRAME_BITS ,
snd_pcm_hw_rule_mulkdiv , ( void * ) 8 ,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES , SNDRV_PCM_HW_PARAM_PERIOD_SIZE , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_FRAME_BITS ,
snd_pcm_hw_rule_mulkdiv , ( void * ) 8 ,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES , SNDRV_PCM_HW_PARAM_BUFFER_SIZE , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_CHANNELS ,
snd_pcm_hw_rule_div , NULL ,
SNDRV_PCM_HW_PARAM_FRAME_BITS , SNDRV_PCM_HW_PARAM_SAMPLE_BITS , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_RATE ,
snd_pcm_hw_rule_mulkdiv , ( void * ) 1000000 ,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE , SNDRV_PCM_HW_PARAM_PERIOD_TIME , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_RATE ,
snd_pcm_hw_rule_mulkdiv , ( void * ) 1000000 ,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE , SNDRV_PCM_HW_PARAM_BUFFER_TIME , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_PERIODS ,
snd_pcm_hw_rule_div , NULL ,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE , SNDRV_PCM_HW_PARAM_PERIOD_SIZE , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_PERIOD_SIZE ,
snd_pcm_hw_rule_div , NULL ,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE , SNDRV_PCM_HW_PARAM_PERIODS , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_PERIOD_SIZE ,
snd_pcm_hw_rule_mulkdiv , ( void * ) 8 ,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES , SNDRV_PCM_HW_PARAM_FRAME_BITS , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_PERIOD_SIZE ,
snd_pcm_hw_rule_muldivk , ( void * ) 1000000 ,
SNDRV_PCM_HW_PARAM_PERIOD_TIME , SNDRV_PCM_HW_PARAM_RATE , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_BUFFER_SIZE ,
snd_pcm_hw_rule_mul , NULL ,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE , SNDRV_PCM_HW_PARAM_PERIODS , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_BUFFER_SIZE ,
snd_pcm_hw_rule_mulkdiv , ( void * ) 8 ,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES , SNDRV_PCM_HW_PARAM_FRAME_BITS , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_BUFFER_SIZE ,
snd_pcm_hw_rule_muldivk , ( void * ) 1000000 ,
SNDRV_PCM_HW_PARAM_BUFFER_TIME , SNDRV_PCM_HW_PARAM_RATE , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_PERIOD_BYTES ,
snd_pcm_hw_rule_muldivk , ( void * ) 8 ,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE , SNDRV_PCM_HW_PARAM_FRAME_BITS , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_BUFFER_BYTES ,
snd_pcm_hw_rule_muldivk , ( void * ) 8 ,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE , SNDRV_PCM_HW_PARAM_FRAME_BITS , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_PERIOD_TIME ,
snd_pcm_hw_rule_mulkdiv , ( void * ) 1000000 ,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE , SNDRV_PCM_HW_PARAM_RATE , - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_BUFFER_TIME ,
snd_pcm_hw_rule_mulkdiv , ( void * ) 1000000 ,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE , SNDRV_PCM_HW_PARAM_RATE , - 1 ) ;
if ( err < 0 )
return err ;
return 0 ;
}
2005-11-17 15:59:38 +03:00
int snd_pcm_hw_constraints_complete ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct snd_pcm_hardware * hw = & runtime - > hw ;
2005-04-17 02:20:36 +04:00
int err ;
unsigned int mask = 0 ;
if ( hw - > info & SNDRV_PCM_INFO_INTERLEAVED )
mask | = 1 < < SNDRV_PCM_ACCESS_RW_INTERLEAVED ;
if ( hw - > info & SNDRV_PCM_INFO_NONINTERLEAVED )
mask | = 1 < < SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ;
if ( hw - > info & SNDRV_PCM_INFO_MMAP ) {
if ( hw - > info & SNDRV_PCM_INFO_INTERLEAVED )
mask | = 1 < < SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ;
if ( hw - > info & SNDRV_PCM_INFO_NONINTERLEAVED )
mask | = 1 < < SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED ;
if ( hw - > info & SNDRV_PCM_INFO_COMPLEX )
mask | = 1 < < SNDRV_PCM_ACCESS_MMAP_COMPLEX ;
}
err = snd_pcm_hw_constraint_mask ( runtime , SNDRV_PCM_HW_PARAM_ACCESS , mask ) ;
2008-08-08 19:09:09 +04:00
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
err = snd_pcm_hw_constraint_mask64 ( runtime , SNDRV_PCM_HW_PARAM_FORMAT , hw - > formats ) ;
2008-08-08 19:09:09 +04:00
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
err = snd_pcm_hw_constraint_mask ( runtime , SNDRV_PCM_HW_PARAM_SUBFORMAT , 1 < < SNDRV_PCM_SUBFORMAT_STD ) ;
2008-08-08 19:09:09 +04:00
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
err = snd_pcm_hw_constraint_minmax ( runtime , SNDRV_PCM_HW_PARAM_CHANNELS ,
hw - > channels_min , hw - > channels_max ) ;
2008-08-08 19:09:09 +04:00
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
err = snd_pcm_hw_constraint_minmax ( runtime , SNDRV_PCM_HW_PARAM_RATE ,
hw - > rate_min , hw - > rate_max ) ;
2009-12-24 03:17:46 +03:00
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
err = snd_pcm_hw_constraint_minmax ( runtime , SNDRV_PCM_HW_PARAM_PERIOD_BYTES ,
hw - > period_bytes_min , hw - > period_bytes_max ) ;
2009-12-24 03:17:46 +03:00
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
err = snd_pcm_hw_constraint_minmax ( runtime , SNDRV_PCM_HW_PARAM_PERIODS ,
hw - > periods_min , hw - > periods_max ) ;
2008-08-08 19:09:09 +04:00
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
err = snd_pcm_hw_constraint_minmax ( runtime , SNDRV_PCM_HW_PARAM_BUFFER_BYTES ,
hw - > period_bytes_min , hw - > buffer_bytes_max ) ;
2008-08-08 19:09:09 +04:00
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_BUFFER_BYTES ,
snd_pcm_hw_rule_buffer_bytes_max , substream ,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES , - 1 ) ;
if ( err < 0 )
return err ;
/* FIXME: remove */
if ( runtime - > dma_bytes ) {
err = snd_pcm_hw_constraint_minmax ( runtime , SNDRV_PCM_HW_PARAM_BUFFER_BYTES , 0 , runtime - > dma_bytes ) ;
2008-08-08 19:09:09 +04:00
if ( err < 0 )
2012-11-21 13:06:55 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
if ( ! ( hw - > rates & ( SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS ) ) ) {
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_RATE ,
snd_pcm_hw_rule_rate , hw ,
SNDRV_PCM_HW_PARAM_RATE , - 1 ) ;
if ( err < 0 )
return err ;
}
/* FIXME: this belong to lowlevel */
snd_pcm_hw_constraint_integer ( runtime , SNDRV_PCM_HW_PARAM_PERIOD_SIZE ) ;
return 0 ;
}
2006-03-27 18:40:49 +04:00
static void pcm_release_private ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
snd_pcm_unlink ( substream ) ;
2006-03-27 18:40:49 +04:00
}
void snd_pcm_release_substream ( struct snd_pcm_substream * substream )
{
2006-04-28 17:13:41 +04:00
substream - > ref_count - - ;
if ( substream - > ref_count > 0 )
return ;
2006-03-27 18:40:49 +04:00
snd_pcm_drop ( substream ) ;
if ( substream - > hw_opened ) {
2005-04-17 02:20:36 +04:00
if ( substream - > ops - > hw_free ! = NULL )
substream - > ops - > hw_free ( substream ) ;
substream - > ops - > close ( substream ) ;
2006-03-27 18:40:49 +04:00
substream - > hw_opened = 0 ;
2005-04-17 02:20:36 +04:00
}
2010-09-17 00:52:32 +04:00
if ( pm_qos_request_active ( & substream - > latency_pm_qos_req ) )
pm_qos_remove_request ( & substream - > latency_pm_qos_req ) ;
2006-04-06 21:47:42 +04:00
if ( substream - > pcm_release ) {
substream - > pcm_release ( substream ) ;
substream - > pcm_release = NULL ;
}
2006-03-27 18:40:49 +04:00
snd_pcm_detach_substream ( substream ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_release_substream ) ;
2006-03-27 18:40:49 +04:00
int snd_pcm_open_substream ( struct snd_pcm * pcm , int stream ,
struct file * file ,
struct snd_pcm_substream * * rsubstream )
{
struct snd_pcm_substream * substream ;
int err ;
err = snd_pcm_attach_substream ( pcm , stream , file , & substream ) ;
if ( err < 0 )
return err ;
2006-04-28 17:13:41 +04:00
if ( substream - > ref_count > 1 ) {
* rsubstream = substream ;
return 0 ;
}
2006-03-27 18:40:49 +04:00
err = snd_pcm_hw_constraints_init ( substream ) ;
if ( err < 0 ) {
snd_printd ( " snd_pcm_hw_constraints_init failed \n " ) ;
goto error ;
}
if ( ( err = substream - > ops - > open ( substream ) ) < 0 )
goto error ;
substream - > hw_opened = 1 ;
err = snd_pcm_hw_constraints_complete ( substream ) ;
if ( err < 0 ) {
snd_printd ( " snd_pcm_hw_constraints_complete failed \n " ) ;
goto error ;
}
* rsubstream = substream ;
2005-04-17 02:20:36 +04:00
return 0 ;
2006-03-27 18:40:49 +04:00
error :
snd_pcm_release_substream ( substream ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_open_substream ) ;
2005-04-17 02:20:36 +04:00
static int snd_pcm_open_file ( struct file * file ,
2005-11-17 15:59:38 +03:00
struct snd_pcm * pcm ,
2011-10-10 06:31:48 +04:00
int stream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_file * pcm_file ;
struct snd_pcm_substream * substream ;
2006-03-27 18:40:49 +04:00
int err ;
2005-04-17 02:20:36 +04:00
2006-03-27 18:40:49 +04:00
err = snd_pcm_open_substream ( pcm , stream , file , & substream ) ;
if ( err < 0 )
return err ;
2006-07-31 18:51:51 +04:00
pcm_file = kzalloc ( sizeof ( * pcm_file ) , GFP_KERNEL ) ;
if ( pcm_file = = NULL ) {
snd_pcm_release_substream ( substream ) ;
return - ENOMEM ;
}
pcm_file - > substream = substream ;
if ( substream - > ref_count = = 1 ) {
2006-04-28 17:13:41 +04:00
substream - > file = pcm_file ;
substream - > pcm_release = pcm_release_private ;
2005-04-17 02:20:36 +04:00
}
file - > private_data = pcm_file ;
2011-10-10 06:31:48 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-20 16:06:59 +03:00
static int snd_pcm_playback_open ( struct inode * inode , struct file * file )
{
struct snd_pcm * pcm ;
2010-04-13 13:49:04 +04:00
int err = nonseekable_open ( inode , file ) ;
if ( err < 0 )
return err ;
2005-11-20 16:06:59 +03:00
pcm = snd_lookup_minor_data ( iminor ( inode ) ,
SNDRV_DEVICE_TYPE_PCM_PLAYBACK ) ;
2012-10-16 15:05:59 +04:00
err = snd_pcm_open ( file , pcm , SNDRV_PCM_STREAM_PLAYBACK ) ;
2012-11-08 17:36:18 +04:00
if ( pcm )
snd_card_unref ( pcm - > card ) ;
2012-10-16 15:05:59 +04:00
return err ;
2005-11-20 16:06:59 +03:00
}
static int snd_pcm_capture_open ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm * pcm ;
2010-04-13 13:49:04 +04:00
int err = nonseekable_open ( inode , file ) ;
if ( err < 0 )
return err ;
2005-11-20 16:06:59 +03:00
pcm = snd_lookup_minor_data ( iminor ( inode ) ,
SNDRV_DEVICE_TYPE_PCM_CAPTURE ) ;
2012-10-16 15:05:59 +04:00
err = snd_pcm_open ( file , pcm , SNDRV_PCM_STREAM_CAPTURE ) ;
2012-11-08 17:36:18 +04:00
if ( pcm )
snd_card_unref ( pcm - > card ) ;
2012-10-16 15:05:59 +04:00
return err ;
2005-11-20 16:06:59 +03:00
}
static int snd_pcm_open ( struct file * file , struct snd_pcm * pcm , int stream )
{
int err ;
2005-04-17 02:20:36 +04:00
wait_queue_t wait ;
if ( pcm = = NULL ) {
err = - ENODEV ;
goto __error1 ;
}
err = snd_card_file_add ( pcm - > card , file ) ;
if ( err < 0 )
goto __error1 ;
if ( ! try_module_get ( pcm - > card - > module ) ) {
err = - EFAULT ;
goto __error2 ;
}
init_waitqueue_entry ( & wait , current ) ;
add_wait_queue ( & pcm - > open_wait , & wait ) ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & pcm - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
while ( 1 ) {
2011-10-10 06:31:48 +04:00
err = snd_pcm_open_file ( file , pcm , stream ) ;
2005-04-17 02:20:36 +04:00
if ( err > = 0 )
break ;
if ( err = = - EAGAIN ) {
if ( file - > f_flags & O_NONBLOCK ) {
err = - EBUSY ;
break ;
}
} else
break ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
2006-01-16 18:29:08 +03:00
mutex_unlock ( & pcm - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & pcm - > open_mutex ) ;
2012-10-16 18:43:39 +04:00
if ( pcm - > card - > shutdown ) {
err = - ENODEV ;
break ;
}
2005-04-17 02:20:36 +04:00
if ( signal_pending ( current ) ) {
err = - ERESTARTSYS ;
break ;
}
}
remove_wait_queue ( & pcm - > open_wait , & wait ) ;
2006-01-16 18:29:08 +03:00
mutex_unlock ( & pcm - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( err < 0 )
goto __error ;
return err ;
__error :
module_put ( pcm - > card - > module ) ;
__error2 :
snd_card_file_remove ( pcm - > card , file ) ;
__error1 :
return err ;
}
static int snd_pcm_release ( struct inode * inode , struct file * file )
{
2005-11-17 15:59:38 +03:00
struct snd_pcm * pcm ;
struct snd_pcm_substream * substream ;
struct snd_pcm_file * pcm_file ;
2005-04-17 02:20:36 +04:00
pcm_file = file - > private_data ;
substream = pcm_file - > substream ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! substream ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
pcm = substream - > pcm ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & pcm - > open_mutex ) ;
2006-03-27 18:40:49 +04:00
snd_pcm_release_substream ( substream ) ;
2006-07-31 18:51:51 +04:00
kfree ( pcm_file ) ;
2006-01-16 18:29:08 +03:00
mutex_unlock ( & pcm - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
wake_up ( & pcm - > open_wait ) ;
module_put ( pcm - > card - > module ) ;
snd_card_file_remove ( pcm - > card , file ) ;
return 0 ;
}
2005-11-17 15:59:38 +03:00
static snd_pcm_sframes_t snd_pcm_playback_rewind ( struct snd_pcm_substream * substream ,
snd_pcm_uframes_t frames )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
snd_pcm_sframes_t appl_ptr ;
snd_pcm_sframes_t ret ;
snd_pcm_sframes_t hw_avail ;
if ( frames = = 0 )
return 0 ;
snd_pcm_stream_lock_irq ( substream ) ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_PREPARED :
break ;
case SNDRV_PCM_STATE_DRAINING :
case SNDRV_PCM_STATE_RUNNING :
if ( snd_pcm_update_hw_ptr ( substream ) > = 0 )
break ;
/* Fall through */
case SNDRV_PCM_STATE_XRUN :
ret = - EPIPE ;
goto __end ;
2009-08-02 20:14:44 +04:00
case SNDRV_PCM_STATE_SUSPENDED :
ret = - ESTRPIPE ;
goto __end ;
2005-04-17 02:20:36 +04:00
default :
ret = - EBADFD ;
goto __end ;
}
hw_avail = snd_pcm_playback_hw_avail ( runtime ) ;
if ( hw_avail < = 0 ) {
ret = 0 ;
goto __end ;
}
if ( frames > ( snd_pcm_uframes_t ) hw_avail )
frames = hw_avail ;
appl_ptr = runtime - > control - > appl_ptr - frames ;
if ( appl_ptr < 0 )
appl_ptr + = runtime - > boundary ;
runtime - > control - > appl_ptr = appl_ptr ;
ret = frames ;
__end :
snd_pcm_stream_unlock_irq ( substream ) ;
return ret ;
}
2005-11-17 15:59:38 +03:00
static snd_pcm_sframes_t snd_pcm_capture_rewind ( struct snd_pcm_substream * substream ,
snd_pcm_uframes_t frames )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
snd_pcm_sframes_t appl_ptr ;
snd_pcm_sframes_t ret ;
snd_pcm_sframes_t hw_avail ;
if ( frames = = 0 )
return 0 ;
snd_pcm_stream_lock_irq ( substream ) ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_PREPARED :
case SNDRV_PCM_STATE_DRAINING :
break ;
case SNDRV_PCM_STATE_RUNNING :
if ( snd_pcm_update_hw_ptr ( substream ) > = 0 )
break ;
/* Fall through */
case SNDRV_PCM_STATE_XRUN :
ret = - EPIPE ;
goto __end ;
2009-08-02 20:14:44 +04:00
case SNDRV_PCM_STATE_SUSPENDED :
ret = - ESTRPIPE ;
goto __end ;
2005-04-17 02:20:36 +04:00
default :
ret = - EBADFD ;
goto __end ;
}
hw_avail = snd_pcm_capture_hw_avail ( runtime ) ;
if ( hw_avail < = 0 ) {
ret = 0 ;
goto __end ;
}
if ( frames > ( snd_pcm_uframes_t ) hw_avail )
frames = hw_avail ;
appl_ptr = runtime - > control - > appl_ptr - frames ;
if ( appl_ptr < 0 )
appl_ptr + = runtime - > boundary ;
runtime - > control - > appl_ptr = appl_ptr ;
ret = frames ;
__end :
snd_pcm_stream_unlock_irq ( substream ) ;
return ret ;
}
2005-11-17 15:59:38 +03:00
static snd_pcm_sframes_t snd_pcm_playback_forward ( struct snd_pcm_substream * substream ,
snd_pcm_uframes_t frames )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
snd_pcm_sframes_t appl_ptr ;
snd_pcm_sframes_t ret ;
snd_pcm_sframes_t avail ;
if ( frames = = 0 )
return 0 ;
snd_pcm_stream_lock_irq ( substream ) ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_PREPARED :
case SNDRV_PCM_STATE_PAUSED :
break ;
case SNDRV_PCM_STATE_DRAINING :
case SNDRV_PCM_STATE_RUNNING :
if ( snd_pcm_update_hw_ptr ( substream ) > = 0 )
break ;
/* Fall through */
case SNDRV_PCM_STATE_XRUN :
ret = - EPIPE ;
goto __end ;
2009-08-02 20:14:44 +04:00
case SNDRV_PCM_STATE_SUSPENDED :
ret = - ESTRPIPE ;
goto __end ;
2005-04-17 02:20:36 +04:00
default :
ret = - EBADFD ;
goto __end ;
}
avail = snd_pcm_playback_avail ( runtime ) ;
if ( avail < = 0 ) {
ret = 0 ;
goto __end ;
}
if ( frames > ( snd_pcm_uframes_t ) avail )
frames = avail ;
appl_ptr = runtime - > control - > appl_ptr + frames ;
if ( appl_ptr > = ( snd_pcm_sframes_t ) runtime - > boundary )
appl_ptr - = runtime - > boundary ;
runtime - > control - > appl_ptr = appl_ptr ;
ret = frames ;
__end :
snd_pcm_stream_unlock_irq ( substream ) ;
return ret ;
}
2005-11-17 15:59:38 +03:00
static snd_pcm_sframes_t snd_pcm_capture_forward ( struct snd_pcm_substream * substream ,
snd_pcm_uframes_t frames )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
snd_pcm_sframes_t appl_ptr ;
snd_pcm_sframes_t ret ;
snd_pcm_sframes_t avail ;
if ( frames = = 0 )
return 0 ;
snd_pcm_stream_lock_irq ( substream ) ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_PREPARED :
case SNDRV_PCM_STATE_DRAINING :
case SNDRV_PCM_STATE_PAUSED :
break ;
case SNDRV_PCM_STATE_RUNNING :
if ( snd_pcm_update_hw_ptr ( substream ) > = 0 )
break ;
/* Fall through */
case SNDRV_PCM_STATE_XRUN :
ret = - EPIPE ;
goto __end ;
2009-08-02 20:14:44 +04:00
case SNDRV_PCM_STATE_SUSPENDED :
ret = - ESTRPIPE ;
goto __end ;
2005-04-17 02:20:36 +04:00
default :
ret = - EBADFD ;
goto __end ;
}
avail = snd_pcm_capture_avail ( runtime ) ;
if ( avail < = 0 ) {
ret = 0 ;
goto __end ;
}
if ( frames > ( snd_pcm_uframes_t ) avail )
frames = avail ;
appl_ptr = runtime - > control - > appl_ptr + frames ;
if ( appl_ptr > = ( snd_pcm_sframes_t ) runtime - > boundary )
appl_ptr - = runtime - > boundary ;
runtime - > control - > appl_ptr = appl_ptr ;
ret = frames ;
__end :
snd_pcm_stream_unlock_irq ( substream ) ;
return ret ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_hwsync ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
int err ;
snd_pcm_stream_lock_irq ( substream ) ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_DRAINING :
if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE )
goto __badfd ;
2013-10-28 15:40:46 +04:00
/* Fall through */
2005-04-17 02:20:36 +04:00
case SNDRV_PCM_STATE_RUNNING :
if ( ( err = snd_pcm_update_hw_ptr ( substream ) ) < 0 )
break ;
/* Fall through */
case SNDRV_PCM_STATE_PREPARED :
case SNDRV_PCM_STATE_SUSPENDED :
err = 0 ;
break ;
case SNDRV_PCM_STATE_XRUN :
err = - EPIPE ;
break ;
default :
__badfd :
err = - EBADFD ;
break ;
}
snd_pcm_stream_unlock_irq ( substream ) ;
return err ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_delay ( struct snd_pcm_substream * substream ,
snd_pcm_sframes_t __user * res )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
int err ;
snd_pcm_sframes_t n = 0 ;
snd_pcm_stream_lock_irq ( substream ) ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_DRAINING :
if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE )
goto __badfd ;
2013-10-28 15:40:46 +04:00
/* Fall through */
2005-04-17 02:20:36 +04:00
case SNDRV_PCM_STATE_RUNNING :
if ( ( err = snd_pcm_update_hw_ptr ( substream ) ) < 0 )
break ;
/* Fall through */
case SNDRV_PCM_STATE_PREPARED :
case SNDRV_PCM_STATE_SUSPENDED :
err = 0 ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
n = snd_pcm_playback_hw_avail ( runtime ) ;
else
n = snd_pcm_capture_avail ( runtime ) ;
2008-10-13 05:07:14 +04:00
n + = runtime - > delay ;
2005-04-17 02:20:36 +04:00
break ;
case SNDRV_PCM_STATE_XRUN :
err = - EPIPE ;
break ;
default :
__badfd :
err = - EBADFD ;
break ;
}
snd_pcm_stream_unlock_irq ( substream ) ;
if ( ! err )
if ( put_user ( n , res ) )
err = - EFAULT ;
return err ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_sync_ptr ( struct snd_pcm_substream * substream ,
struct snd_pcm_sync_ptr __user * _sync_ptr )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct snd_pcm_sync_ptr sync_ptr ;
volatile struct snd_pcm_mmap_status * status ;
volatile struct snd_pcm_mmap_control * control ;
2005-04-17 02:20:36 +04:00
int err ;
memset ( & sync_ptr , 0 , sizeof ( sync_ptr ) ) ;
if ( get_user ( sync_ptr . flags , ( unsigned __user * ) & ( _sync_ptr - > flags ) ) )
return - EFAULT ;
2005-11-17 15:59:38 +03:00
if ( copy_from_user ( & sync_ptr . c . control , & ( _sync_ptr - > c . control ) , sizeof ( struct snd_pcm_mmap_control ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
status = runtime - > status ;
control = runtime - > control ;
if ( sync_ptr . flags & SNDRV_PCM_SYNC_PTR_HWSYNC ) {
err = snd_pcm_hwsync ( substream ) ;
if ( err < 0 )
return err ;
}
snd_pcm_stream_lock_irq ( substream ) ;
if ( ! ( sync_ptr . flags & SNDRV_PCM_SYNC_PTR_APPL ) )
control - > appl_ptr = sync_ptr . c . control . appl_ptr ;
else
sync_ptr . c . control . appl_ptr = control - > appl_ptr ;
if ( ! ( sync_ptr . flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN ) )
control - > avail_min = sync_ptr . c . control . avail_min ;
else
sync_ptr . c . control . avail_min = control - > avail_min ;
sync_ptr . s . status . state = status - > state ;
sync_ptr . s . status . hw_ptr = status - > hw_ptr ;
sync_ptr . s . status . tstamp = status - > tstamp ;
sync_ptr . s . status . suspended_state = status - > suspended_state ;
snd_pcm_stream_unlock_irq ( substream ) ;
if ( copy_to_user ( _sync_ptr , & sync_ptr , sizeof ( sync_ptr ) ) )
return - EFAULT ;
return 0 ;
}
2007-12-13 12:19:42 +03:00
static int snd_pcm_tstamp ( struct snd_pcm_substream * substream , int __user * _arg )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
int arg ;
if ( get_user ( arg , _arg ) )
return - EFAULT ;
if ( arg < 0 | | arg > SNDRV_PCM_TSTAMP_TYPE_LAST )
return - EINVAL ;
runtime - > tstamp_type = SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY ;
if ( arg = = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC )
runtime - > tstamp_type = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2006-04-28 17:13:41 +04:00
static int snd_pcm_common_ioctl1 ( struct file * file ,
struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
unsigned int cmd , void __user * arg )
{
switch ( cmd ) {
case SNDRV_PCM_IOCTL_PVERSION :
return put_user ( SNDRV_PCM_VERSION , ( int __user * ) arg ) ? - EFAULT : 0 ;
case SNDRV_PCM_IOCTL_INFO :
return snd_pcm_info_user ( substream , arg ) ;
2007-12-17 11:02:22 +03:00
case SNDRV_PCM_IOCTL_TSTAMP : /* just for compatibility */
return 0 ;
2007-12-13 12:19:42 +03:00
case SNDRV_PCM_IOCTL_TTSTAMP :
return snd_pcm_tstamp ( substream , arg ) ;
2005-04-17 02:20:36 +04:00
case SNDRV_PCM_IOCTL_HW_REFINE :
return snd_pcm_hw_refine_user ( substream , arg ) ;
case SNDRV_PCM_IOCTL_HW_PARAMS :
return snd_pcm_hw_params_user ( substream , arg ) ;
case SNDRV_PCM_IOCTL_HW_FREE :
return snd_pcm_hw_free ( substream ) ;
case SNDRV_PCM_IOCTL_SW_PARAMS :
return snd_pcm_sw_params_user ( substream , arg ) ;
case SNDRV_PCM_IOCTL_STATUS :
return snd_pcm_status_user ( substream , arg ) ;
case SNDRV_PCM_IOCTL_CHANNEL_INFO :
return snd_pcm_channel_info_user ( substream , arg ) ;
case SNDRV_PCM_IOCTL_PREPARE :
2006-04-28 17:13:41 +04:00
return snd_pcm_prepare ( substream , file ) ;
2005-04-17 02:20:36 +04:00
case SNDRV_PCM_IOCTL_RESET :
return snd_pcm_reset ( substream ) ;
case SNDRV_PCM_IOCTL_START :
return snd_pcm_action_lock_irq ( & snd_pcm_action_start , substream , SNDRV_PCM_STATE_RUNNING ) ;
case SNDRV_PCM_IOCTL_LINK :
return snd_pcm_link ( substream , ( int ) ( unsigned long ) arg ) ;
case SNDRV_PCM_IOCTL_UNLINK :
return snd_pcm_unlink ( substream ) ;
case SNDRV_PCM_IOCTL_RESUME :
return snd_pcm_resume ( substream ) ;
case SNDRV_PCM_IOCTL_XRUN :
return snd_pcm_xrun ( substream ) ;
case SNDRV_PCM_IOCTL_HWSYNC :
return snd_pcm_hwsync ( substream ) ;
case SNDRV_PCM_IOCTL_DELAY :
return snd_pcm_delay ( substream , arg ) ;
case SNDRV_PCM_IOCTL_SYNC_PTR :
return snd_pcm_sync_ptr ( substream , arg ) ;
2005-12-01 12:51:58 +03:00
# ifdef CONFIG_SND_SUPPORT_OLD_API
2005-04-17 02:20:36 +04:00
case SNDRV_PCM_IOCTL_HW_REFINE_OLD :
return snd_pcm_hw_refine_old_user ( substream , arg ) ;
case SNDRV_PCM_IOCTL_HW_PARAMS_OLD :
return snd_pcm_hw_params_old_user ( substream , arg ) ;
2005-12-01 12:51:58 +03:00
# endif
2005-04-17 02:20:36 +04:00
case SNDRV_PCM_IOCTL_DRAIN :
2009-08-20 18:40:16 +04:00
return snd_pcm_drain ( substream , file ) ;
2005-04-17 02:20:36 +04:00
case SNDRV_PCM_IOCTL_DROP :
return snd_pcm_drop ( substream ) ;
2006-02-21 16:14:50 +03:00
case SNDRV_PCM_IOCTL_PAUSE :
{
int res ;
snd_pcm_stream_lock_irq ( substream ) ;
res = snd_pcm_pause ( substream , ( int ) ( unsigned long ) arg ) ;
snd_pcm_stream_unlock_irq ( substream ) ;
return res ;
}
2005-04-17 02:20:36 +04:00
}
snd_printd ( " unknown ioctl = 0x%x \n " , cmd ) ;
return - ENOTTY ;
}
2006-04-28 17:13:41 +04:00
static int snd_pcm_playback_ioctl1 ( struct file * file ,
struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
unsigned int cmd , void __user * arg )
{
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! substream ) )
return - ENXIO ;
if ( snd_BUG_ON ( substream - > stream ! = SNDRV_PCM_STREAM_PLAYBACK ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case SNDRV_PCM_IOCTL_WRITEI_FRAMES :
{
2005-11-17 15:59:38 +03:00
struct snd_xferi xferi ;
struct snd_xferi __user * _xferi = arg ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
snd_pcm_sframes_t result ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN )
return - EBADFD ;
if ( put_user ( 0 , & _xferi - > result ) )
return - EFAULT ;
if ( copy_from_user ( & xferi , _xferi , sizeof ( xferi ) ) )
return - EFAULT ;
result = snd_pcm_lib_write ( substream , xferi . buf , xferi . frames ) ;
__put_user ( result , & _xferi - > result ) ;
return result < 0 ? result : 0 ;
}
case SNDRV_PCM_IOCTL_WRITEN_FRAMES :
{
2005-11-17 15:59:38 +03:00
struct snd_xfern xfern ;
struct snd_xfern __user * _xfern = arg ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
void __user * * bufs ;
snd_pcm_sframes_t result ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN )
return - EBADFD ;
if ( runtime - > channels > 128 )
return - EINVAL ;
if ( put_user ( 0 , & _xfern - > result ) )
return - EFAULT ;
if ( copy_from_user ( & xfern , _xfern , sizeof ( xfern ) ) )
return - EFAULT ;
2009-04-10 05:43:08 +04:00
bufs = memdup_user ( xfern . bufs ,
sizeof ( void * ) * runtime - > channels ) ;
if ( IS_ERR ( bufs ) )
return PTR_ERR ( bufs ) ;
2005-04-17 02:20:36 +04:00
result = snd_pcm_lib_writev ( substream , bufs , xfern . frames ) ;
kfree ( bufs ) ;
__put_user ( result , & _xfern - > result ) ;
return result < 0 ? result : 0 ;
}
case SNDRV_PCM_IOCTL_REWIND :
{
snd_pcm_uframes_t frames ;
snd_pcm_uframes_t __user * _frames = arg ;
snd_pcm_sframes_t result ;
if ( get_user ( frames , _frames ) )
return - EFAULT ;
if ( put_user ( 0 , _frames ) )
return - EFAULT ;
result = snd_pcm_playback_rewind ( substream , frames ) ;
__put_user ( result , _frames ) ;
return result < 0 ? result : 0 ;
}
case SNDRV_PCM_IOCTL_FORWARD :
{
snd_pcm_uframes_t frames ;
snd_pcm_uframes_t __user * _frames = arg ;
snd_pcm_sframes_t result ;
if ( get_user ( frames , _frames ) )
return - EFAULT ;
if ( put_user ( 0 , _frames ) )
return - EFAULT ;
result = snd_pcm_playback_forward ( substream , frames ) ;
__put_user ( result , _frames ) ;
return result < 0 ? result : 0 ;
}
}
2006-04-28 17:13:41 +04:00
return snd_pcm_common_ioctl1 ( file , substream , cmd , arg ) ;
2005-04-17 02:20:36 +04:00
}
2006-04-28 17:13:41 +04:00
static int snd_pcm_capture_ioctl1 ( struct file * file ,
struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
unsigned int cmd , void __user * arg )
{
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! substream ) )
return - ENXIO ;
if ( snd_BUG_ON ( substream - > stream ! = SNDRV_PCM_STREAM_CAPTURE ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case SNDRV_PCM_IOCTL_READI_FRAMES :
{
2005-11-17 15:59:38 +03:00
struct snd_xferi xferi ;
struct snd_xferi __user * _xferi = arg ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
snd_pcm_sframes_t result ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN )
return - EBADFD ;
if ( put_user ( 0 , & _xferi - > result ) )
return - EFAULT ;
if ( copy_from_user ( & xferi , _xferi , sizeof ( xferi ) ) )
return - EFAULT ;
result = snd_pcm_lib_read ( substream , xferi . buf , xferi . frames ) ;
__put_user ( result , & _xferi - > result ) ;
return result < 0 ? result : 0 ;
}
case SNDRV_PCM_IOCTL_READN_FRAMES :
{
2005-11-17 15:59:38 +03:00
struct snd_xfern xfern ;
struct snd_xfern __user * _xfern = arg ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
void * bufs ;
snd_pcm_sframes_t result ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN )
return - EBADFD ;
if ( runtime - > channels > 128 )
return - EINVAL ;
if ( put_user ( 0 , & _xfern - > result ) )
return - EFAULT ;
if ( copy_from_user ( & xfern , _xfern , sizeof ( xfern ) ) )
return - EFAULT ;
2009-04-10 05:43:08 +04:00
bufs = memdup_user ( xfern . bufs ,
sizeof ( void * ) * runtime - > channels ) ;
if ( IS_ERR ( bufs ) )
return PTR_ERR ( bufs ) ;
2005-04-17 02:20:36 +04:00
result = snd_pcm_lib_readv ( substream , bufs , xfern . frames ) ;
kfree ( bufs ) ;
__put_user ( result , & _xfern - > result ) ;
return result < 0 ? result : 0 ;
}
case SNDRV_PCM_IOCTL_REWIND :
{
snd_pcm_uframes_t frames ;
snd_pcm_uframes_t __user * _frames = arg ;
snd_pcm_sframes_t result ;
if ( get_user ( frames , _frames ) )
return - EFAULT ;
if ( put_user ( 0 , _frames ) )
return - EFAULT ;
result = snd_pcm_capture_rewind ( substream , frames ) ;
__put_user ( result , _frames ) ;
return result < 0 ? result : 0 ;
}
case SNDRV_PCM_IOCTL_FORWARD :
{
snd_pcm_uframes_t frames ;
snd_pcm_uframes_t __user * _frames = arg ;
snd_pcm_sframes_t result ;
if ( get_user ( frames , _frames ) )
return - EFAULT ;
if ( put_user ( 0 , _frames ) )
return - EFAULT ;
result = snd_pcm_capture_forward ( substream , frames ) ;
__put_user ( result , _frames ) ;
return result < 0 ? result : 0 ;
}
}
2006-04-28 17:13:41 +04:00
return snd_pcm_common_ioctl1 ( file , substream , cmd , arg ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-17 15:59:38 +03:00
static long snd_pcm_playback_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_file * pcm_file ;
2005-04-17 02:20:36 +04:00
pcm_file = file - > private_data ;
if ( ( ( cmd > > 8 ) & 0xff ) ! = ' A ' )
return - ENOTTY ;
2006-04-28 17:13:41 +04:00
return snd_pcm_playback_ioctl1 ( file , pcm_file - > substream , cmd ,
( void __user * ) arg ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-17 15:59:38 +03:00
static long snd_pcm_capture_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_file * pcm_file ;
2005-04-17 02:20:36 +04:00
pcm_file = file - > private_data ;
if ( ( ( cmd > > 8 ) & 0xff ) ! = ' A ' )
return - ENOTTY ;
2006-04-28 17:13:41 +04:00
return snd_pcm_capture_ioctl1 ( file , pcm_file - > substream , cmd ,
( void __user * ) arg ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-27 18:22:45 +04:00
int snd_pcm_kernel_ioctl ( struct snd_pcm_substream * substream ,
unsigned int cmd , void * arg )
2005-04-17 02:20:36 +04:00
{
mm_segment_t fs ;
int result ;
fs = snd_enter_user ( ) ;
switch ( substream - > stream ) {
case SNDRV_PCM_STREAM_PLAYBACK :
2006-04-28 17:13:41 +04:00
result = snd_pcm_playback_ioctl1 ( NULL , substream , cmd ,
( void __user * ) arg ) ;
2006-03-27 18:22:45 +04:00
break ;
2005-04-17 02:20:36 +04:00
case SNDRV_PCM_STREAM_CAPTURE :
2006-04-28 17:13:41 +04:00
result = snd_pcm_capture_ioctl1 ( NULL , substream , cmd ,
( void __user * ) arg ) ;
2006-03-27 18:22:45 +04:00
break ;
2005-04-17 02:20:36 +04:00
default :
2006-03-27 18:22:45 +04:00
result = - EINVAL ;
break ;
2005-04-17 02:20:36 +04:00
}
2006-03-27 18:22:45 +04:00
snd_leave_user ( fs ) ;
return result ;
2005-04-17 02:20:36 +04:00
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_kernel_ioctl ) ;
2005-11-17 15:59:38 +03:00
static ssize_t snd_pcm_read ( struct file * file , char __user * buf , size_t count ,
loff_t * offset )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_file * pcm_file ;
struct snd_pcm_substream * substream ;
struct snd_pcm_runtime * runtime ;
2005-04-17 02:20:36 +04:00
snd_pcm_sframes_t result ;
pcm_file = file - > private_data ;
substream = pcm_file - > substream ;
2008-08-08 19:09:09 +04:00
if ( PCM_RUNTIME_CHECK ( substream ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN )
return - EBADFD ;
if ( ! frame_aligned ( runtime , count ) )
return - EINVAL ;
count = bytes_to_frames ( runtime , count ) ;
result = snd_pcm_lib_read ( substream , buf , count ) ;
if ( result > 0 )
result = frames_to_bytes ( runtime , result ) ;
return result ;
}
2005-11-17 15:59:38 +03:00
static ssize_t snd_pcm_write ( struct file * file , const char __user * buf ,
size_t count , loff_t * offset )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_file * pcm_file ;
struct snd_pcm_substream * substream ;
struct snd_pcm_runtime * runtime ;
2005-04-17 02:20:36 +04:00
snd_pcm_sframes_t result ;
pcm_file = file - > private_data ;
substream = pcm_file - > substream ;
2008-08-08 19:09:09 +04:00
if ( PCM_RUNTIME_CHECK ( substream ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
2008-08-08 19:09:09 +04:00
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN )
return - EBADFD ;
if ( ! frame_aligned ( runtime , count ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
count = bytes_to_frames ( runtime , count ) ;
result = snd_pcm_lib_write ( substream , buf , count ) ;
if ( result > 0 )
result = frames_to_bytes ( runtime , result ) ;
return result ;
}
2006-10-01 10:28:47 +04:00
static ssize_t snd_pcm_aio_read ( struct kiocb * iocb , const struct iovec * iov ,
unsigned long nr_segs , loff_t pos )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_file * pcm_file ;
struct snd_pcm_substream * substream ;
struct snd_pcm_runtime * runtime ;
2005-04-17 02:20:36 +04:00
snd_pcm_sframes_t result ;
unsigned long i ;
void __user * * bufs ;
snd_pcm_uframes_t frames ;
2006-10-01 10:28:47 +04:00
pcm_file = iocb - > ki_filp - > private_data ;
2005-04-17 02:20:36 +04:00
substream = pcm_file - > substream ;
2008-08-08 19:09:09 +04:00
if ( PCM_RUNTIME_CHECK ( substream ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN )
return - EBADFD ;
2006-10-01 10:28:47 +04:00
if ( nr_segs > 1024 | | nr_segs ! = runtime - > channels )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2006-10-01 10:28:47 +04:00
if ( ! frame_aligned ( runtime , iov - > iov_len ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2006-10-01 10:28:47 +04:00
frames = bytes_to_samples ( runtime , iov - > iov_len ) ;
bufs = kmalloc ( sizeof ( void * ) * nr_segs , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( bufs = = NULL )
return - ENOMEM ;
2006-10-01 10:28:47 +04:00
for ( i = 0 ; i < nr_segs ; + + i )
bufs [ i ] = iov [ i ] . iov_base ;
2005-04-17 02:20:36 +04:00
result = snd_pcm_lib_readv ( substream , bufs , frames ) ;
if ( result > 0 )
result = frames_to_bytes ( runtime , result ) ;
kfree ( bufs ) ;
return result ;
}
2006-10-01 10:28:47 +04:00
static ssize_t snd_pcm_aio_write ( struct kiocb * iocb , const struct iovec * iov ,
unsigned long nr_segs , loff_t pos )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_file * pcm_file ;
struct snd_pcm_substream * substream ;
struct snd_pcm_runtime * runtime ;
2005-04-17 02:20:36 +04:00
snd_pcm_sframes_t result ;
unsigned long i ;
void __user * * bufs ;
snd_pcm_uframes_t frames ;
2006-10-01 10:28:47 +04:00
pcm_file = iocb - > ki_filp - > private_data ;
2005-04-17 02:20:36 +04:00
substream = pcm_file - > substream ;
2008-08-08 19:09:09 +04:00
if ( PCM_RUNTIME_CHECK ( substream ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
2008-08-08 19:09:09 +04:00
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN )
return - EBADFD ;
2006-10-01 10:28:47 +04:00
if ( nr_segs > 128 | | nr_segs ! = runtime - > channels | |
2008-08-08 19:09:09 +04:00
! frame_aligned ( runtime , iov - > iov_len ) )
return - EINVAL ;
2006-10-01 10:28:47 +04:00
frames = bytes_to_samples ( runtime , iov - > iov_len ) ;
bufs = kmalloc ( sizeof ( void * ) * nr_segs , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( bufs = = NULL )
return - ENOMEM ;
2006-10-01 10:28:47 +04:00
for ( i = 0 ; i < nr_segs ; + + i )
bufs [ i ] = iov [ i ] . iov_base ;
2005-04-17 02:20:36 +04:00
result = snd_pcm_lib_writev ( substream , bufs , frames ) ;
if ( result > 0 )
result = frames_to_bytes ( runtime , result ) ;
kfree ( bufs ) ;
return result ;
}
static unsigned int snd_pcm_playback_poll ( struct file * file , poll_table * wait )
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_file * pcm_file ;
struct snd_pcm_substream * substream ;
struct snd_pcm_runtime * runtime ;
2005-04-17 02:20:36 +04:00
unsigned int mask ;
snd_pcm_uframes_t avail ;
pcm_file = file - > private_data ;
substream = pcm_file - > substream ;
2008-08-08 19:09:09 +04:00
if ( PCM_RUNTIME_CHECK ( substream ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
poll_wait ( file , & runtime - > sleep , wait ) ;
snd_pcm_stream_lock_irq ( substream ) ;
avail = snd_pcm_playback_avail ( runtime ) ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_RUNNING :
case SNDRV_PCM_STATE_PREPARED :
case SNDRV_PCM_STATE_PAUSED :
if ( avail > = runtime - > control - > avail_min ) {
mask = POLLOUT | POLLWRNORM ;
break ;
}
/* Fall through */
case SNDRV_PCM_STATE_DRAINING :
mask = 0 ;
break ;
default :
mask = POLLOUT | POLLWRNORM | POLLERR ;
break ;
}
snd_pcm_stream_unlock_irq ( substream ) ;
return mask ;
}
static unsigned int snd_pcm_capture_poll ( struct file * file , poll_table * wait )
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_file * pcm_file ;
struct snd_pcm_substream * substream ;
struct snd_pcm_runtime * runtime ;
2005-04-17 02:20:36 +04:00
unsigned int mask ;
snd_pcm_uframes_t avail ;
pcm_file = file - > private_data ;
substream = pcm_file - > substream ;
2008-08-08 19:09:09 +04:00
if ( PCM_RUNTIME_CHECK ( substream ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
poll_wait ( file , & runtime - > sleep , wait ) ;
snd_pcm_stream_lock_irq ( substream ) ;
avail = snd_pcm_capture_avail ( runtime ) ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_RUNNING :
case SNDRV_PCM_STATE_PREPARED :
case SNDRV_PCM_STATE_PAUSED :
if ( avail > = runtime - > control - > avail_min ) {
mask = POLLIN | POLLRDNORM ;
break ;
}
mask = 0 ;
break ;
case SNDRV_PCM_STATE_DRAINING :
if ( avail > 0 ) {
mask = POLLIN | POLLRDNORM ;
break ;
}
/* Fall through */
default :
mask = POLLIN | POLLRDNORM | POLLERR ;
break ;
}
snd_pcm_stream_unlock_irq ( substream ) ;
return mask ;
}
/*
* mmap support
*/
/*
* Only on coherent architectures , we can mmap the status and the control records
* for effcient data transfer . On others , we have to use HWSYNC ioctl . . .
*/
# if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_ALPHA)
/*
* mmap status record
*/
2007-12-13 18:15:00 +03:00
static int snd_pcm_mmap_status_fault ( struct vm_area_struct * area ,
struct vm_fault * vmf )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream = area - > vm_private_data ;
struct snd_pcm_runtime * runtime ;
2005-04-17 02:20:36 +04:00
if ( substream = = NULL )
2007-12-13 18:15:00 +03:00
return VM_FAULT_SIGBUS ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
2007-12-13 18:15:00 +03:00
vmf - > page = virt_to_page ( runtime - > status ) ;
get_page ( vmf - > page ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-09-27 22:29:37 +04:00
static const struct vm_operations_struct snd_pcm_vm_ops_status =
2005-04-17 02:20:36 +04:00
{
2007-12-13 18:15:00 +03:00
. fault = snd_pcm_mmap_status_fault ,
2005-04-17 02:20:36 +04:00
} ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_mmap_status ( struct snd_pcm_substream * substream , struct file * file ,
2005-04-17 02:20:36 +04:00
struct vm_area_struct * area )
{
long size ;
if ( ! ( area - > vm_flags & VM_READ ) )
return - EINVAL ;
size = area - > vm_end - area - > vm_start ;
2005-11-17 15:59:38 +03:00
if ( size ! = PAGE_ALIGN ( sizeof ( struct snd_pcm_mmap_status ) ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
area - > vm_ops = & snd_pcm_vm_ops_status ;
area - > vm_private_data = substream ;
mm: kill vma flag VM_RESERVED and mm->reserved_vm counter
A long time ago, in v2.4, VM_RESERVED kept swapout process off VMA,
currently it lost original meaning but still has some effects:
| effect | alternative flags
-+------------------------+---------------------------------------------
1| account as reserved_vm | VM_IO
2| skip in core dump | VM_IO, VM_DONTDUMP
3| do not merge or expand | VM_IO, VM_DONTEXPAND, VM_HUGETLB, VM_PFNMAP
4| do not mlock | VM_IO, VM_DONTEXPAND, VM_HUGETLB, VM_PFNMAP
This patch removes reserved_vm counter from mm_struct. Seems like nobody
cares about it, it does not exported into userspace directly, it only
reduces total_vm showed in proc.
Thus VM_RESERVED can be replaced with VM_IO or pair VM_DONTEXPAND | VM_DONTDUMP.
remap_pfn_range() and io_remap_pfn_range() set VM_IO|VM_DONTEXPAND|VM_DONTDUMP.
remap_vmalloc_range() set VM_DONTEXPAND | VM_DONTDUMP.
[akpm@linux-foundation.org: drivers/vfio/pci/vfio_pci.c fixup]
Signed-off-by: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Carsten Otte <cotte@de.ibm.com>
Cc: Chris Metcalf <cmetcalf@tilera.com>
Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: Eric Paris <eparis@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Jason Baron <jbaron@redhat.com>
Cc: Kentaro Takeda <takedakn@nttdata.co.jp>
Cc: Matt Helsley <matthltc@us.ibm.com>
Cc: Nick Piggin <npiggin@kernel.dk>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Suresh Siddha <suresh.b.siddha@intel.com>
Cc: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Cc: Venkatesh Pallipadi <venki@google.com>
Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-10-09 03:29:02 +04:00
area - > vm_flags | = VM_DONTEXPAND | VM_DONTDUMP ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* mmap control record
*/
2007-12-13 18:15:00 +03:00
static int snd_pcm_mmap_control_fault ( struct vm_area_struct * area ,
struct vm_fault * vmf )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream = area - > vm_private_data ;
struct snd_pcm_runtime * runtime ;
2005-04-17 02:20:36 +04:00
if ( substream = = NULL )
2007-12-13 18:15:00 +03:00
return VM_FAULT_SIGBUS ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
2007-12-13 18:15:00 +03:00
vmf - > page = virt_to_page ( runtime - > control ) ;
get_page ( vmf - > page ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-09-27 22:29:37 +04:00
static const struct vm_operations_struct snd_pcm_vm_ops_control =
2005-04-17 02:20:36 +04:00
{
2007-12-13 18:15:00 +03:00
. fault = snd_pcm_mmap_control_fault ,
2005-04-17 02:20:36 +04:00
} ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_mmap_control ( struct snd_pcm_substream * substream , struct file * file ,
2005-04-17 02:20:36 +04:00
struct vm_area_struct * area )
{
long size ;
if ( ! ( area - > vm_flags & VM_READ ) )
return - EINVAL ;
size = area - > vm_end - area - > vm_start ;
2005-11-17 15:59:38 +03:00
if ( size ! = PAGE_ALIGN ( sizeof ( struct snd_pcm_mmap_control ) ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
area - > vm_ops = & snd_pcm_vm_ops_control ;
area - > vm_private_data = substream ;
mm: kill vma flag VM_RESERVED and mm->reserved_vm counter
A long time ago, in v2.4, VM_RESERVED kept swapout process off VMA,
currently it lost original meaning but still has some effects:
| effect | alternative flags
-+------------------------+---------------------------------------------
1| account as reserved_vm | VM_IO
2| skip in core dump | VM_IO, VM_DONTDUMP
3| do not merge or expand | VM_IO, VM_DONTEXPAND, VM_HUGETLB, VM_PFNMAP
4| do not mlock | VM_IO, VM_DONTEXPAND, VM_HUGETLB, VM_PFNMAP
This patch removes reserved_vm counter from mm_struct. Seems like nobody
cares about it, it does not exported into userspace directly, it only
reduces total_vm showed in proc.
Thus VM_RESERVED can be replaced with VM_IO or pair VM_DONTEXPAND | VM_DONTDUMP.
remap_pfn_range() and io_remap_pfn_range() set VM_IO|VM_DONTEXPAND|VM_DONTDUMP.
remap_vmalloc_range() set VM_DONTEXPAND | VM_DONTDUMP.
[akpm@linux-foundation.org: drivers/vfio/pci/vfio_pci.c fixup]
Signed-off-by: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Carsten Otte <cotte@de.ibm.com>
Cc: Chris Metcalf <cmetcalf@tilera.com>
Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: Eric Paris <eparis@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Jason Baron <jbaron@redhat.com>
Cc: Kentaro Takeda <takedakn@nttdata.co.jp>
Cc: Matt Helsley <matthltc@us.ibm.com>
Cc: Nick Piggin <npiggin@kernel.dk>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Suresh Siddha <suresh.b.siddha@intel.com>
Cc: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Cc: Venkatesh Pallipadi <venki@google.com>
Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-10-09 03:29:02 +04:00
area - > vm_flags | = VM_DONTEXPAND | VM_DONTDUMP ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
# else /* ! coherent mmap */
/*
* don ' t support mmap for status and control records .
*/
2005-11-17 15:59:38 +03:00
static int snd_pcm_mmap_status ( struct snd_pcm_substream * substream , struct file * file ,
2005-04-17 02:20:36 +04:00
struct vm_area_struct * area )
{
return - ENXIO ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_mmap_control ( struct snd_pcm_substream * substream , struct file * file ,
2005-04-17 02:20:36 +04:00
struct vm_area_struct * area )
{
return - ENXIO ;
}
# endif /* coherent mmap */
2009-11-26 14:43:39 +03:00
static inline struct page *
snd_pcm_default_page_ops ( struct snd_pcm_substream * substream , unsigned long ofs )
{
void * vaddr = substream - > runtime - > dma_area + ofs ;
2009-11-26 14:50:01 +03:00
# if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
if ( substream - > dma_buffer . dev . type = = SNDRV_DMA_TYPE_DEV )
return virt_to_page ( CAC_ADDR ( vaddr ) ) ;
2009-11-26 17:04:24 +03:00
# endif
# if defined(CONFIG_PPC32) && defined(CONFIG_NOT_COHERENT_CACHE)
if ( substream - > dma_buffer . dev . type = = SNDRV_DMA_TYPE_DEV ) {
dma_addr_t addr = substream - > runtime - > dma_addr + ofs ;
addr - = get_dma_offset ( substream - > dma_buffer . dev . dev ) ;
/* assume dma_handle set via pfn_to_phys() in
* mm / dma - noncoherent . c
*/
return pfn_to_page ( addr > > PAGE_SHIFT ) ;
}
2009-11-26 14:50:01 +03:00
# endif
2009-11-26 14:43:39 +03:00
return virt_to_page ( vaddr ) ;
}
2005-04-17 02:20:36 +04:00
/*
2007-12-13 18:15:00 +03:00
* fault callback for mmapping a RAM page
2005-04-17 02:20:36 +04:00
*/
2007-12-13 18:15:00 +03:00
static int snd_pcm_mmap_data_fault ( struct vm_area_struct * area ,
struct vm_fault * vmf )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream = area - > vm_private_data ;
struct snd_pcm_runtime * runtime ;
2005-04-17 02:20:36 +04:00
unsigned long offset ;
struct page * page ;
size_t dma_bytes ;
if ( substream = = NULL )
2007-12-13 18:15:00 +03:00
return VM_FAULT_SIGBUS ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
2007-12-13 18:15:00 +03:00
offset = vmf - > pgoff < < PAGE_SHIFT ;
2005-04-17 02:20:36 +04:00
dma_bytes = PAGE_ALIGN ( runtime - > dma_bytes ) ;
if ( offset > dma_bytes - PAGE_SIZE )
2007-12-13 18:15:00 +03:00
return VM_FAULT_SIGBUS ;
2009-11-26 14:43:39 +03:00
if ( substream - > ops - > page )
2005-04-17 02:20:36 +04:00
page = substream - > ops - > page ( substream , offset ) ;
2009-11-26 14:43:39 +03:00
else
page = snd_pcm_default_page_ops ( substream , offset ) ;
if ( ! page )
return VM_FAULT_SIGBUS ;
2005-10-30 04:16:12 +03:00
get_page ( page ) ;
2007-12-13 18:15:00 +03:00
vmf - > page = page ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-11-26 14:40:21 +03:00
static const struct vm_operations_struct snd_pcm_vm_ops_data = {
. open = snd_pcm_mmap_data_open ,
. close = snd_pcm_mmap_data_close ,
} ;
static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = {
2005-04-17 02:20:36 +04:00
. open = snd_pcm_mmap_data_open ,
. close = snd_pcm_mmap_data_close ,
2007-12-13 18:15:00 +03:00
. fault = snd_pcm_mmap_data_fault ,
2005-04-17 02:20:36 +04:00
} ;
2009-11-26 14:40:21 +03:00
# ifndef ARCH_HAS_DMA_MMAP_COHERENT
/* This should be defined / handled globally! */
# ifdef CONFIG_ARM
# define ARCH_HAS_DMA_MMAP_COHERENT
# endif
# endif
2005-04-17 02:20:36 +04:00
/*
* mmap the DMA buffer on RAM
*/
2011-09-28 19:12:59 +04:00
int snd_pcm_lib_default_mmap ( struct snd_pcm_substream * substream ,
struct vm_area_struct * area )
2005-04-17 02:20:36 +04:00
{
mm: kill vma flag VM_RESERVED and mm->reserved_vm counter
A long time ago, in v2.4, VM_RESERVED kept swapout process off VMA,
currently it lost original meaning but still has some effects:
| effect | alternative flags
-+------------------------+---------------------------------------------
1| account as reserved_vm | VM_IO
2| skip in core dump | VM_IO, VM_DONTDUMP
3| do not merge or expand | VM_IO, VM_DONTEXPAND, VM_HUGETLB, VM_PFNMAP
4| do not mlock | VM_IO, VM_DONTEXPAND, VM_HUGETLB, VM_PFNMAP
This patch removes reserved_vm counter from mm_struct. Seems like nobody
cares about it, it does not exported into userspace directly, it only
reduces total_vm showed in proc.
Thus VM_RESERVED can be replaced with VM_IO or pair VM_DONTEXPAND | VM_DONTDUMP.
remap_pfn_range() and io_remap_pfn_range() set VM_IO|VM_DONTEXPAND|VM_DONTDUMP.
remap_vmalloc_range() set VM_DONTEXPAND | VM_DONTDUMP.
[akpm@linux-foundation.org: drivers/vfio/pci/vfio_pci.c fixup]
Signed-off-by: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Carsten Otte <cotte@de.ibm.com>
Cc: Chris Metcalf <cmetcalf@tilera.com>
Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: Eric Paris <eparis@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Jason Baron <jbaron@redhat.com>
Cc: Kentaro Takeda <takedakn@nttdata.co.jp>
Cc: Matt Helsley <matthltc@us.ibm.com>
Cc: Nick Piggin <npiggin@kernel.dk>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Suresh Siddha <suresh.b.siddha@intel.com>
Cc: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Cc: Venkatesh Pallipadi <venki@google.com>
Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-10-09 03:29:02 +04:00
area - > vm_flags | = VM_DONTEXPAND | VM_DONTDUMP ;
2013-10-24 16:25:32 +04:00
# ifdef CONFIG_GENERIC_ALLOCATOR
2013-10-23 07:47:43 +04:00
if ( substream - > dma_buffer . dev . type = = SNDRV_DMA_TYPE_DEV_IRAM ) {
area - > vm_page_prot = pgprot_writecombine ( area - > vm_page_prot ) ;
return remap_pfn_range ( area , area - > vm_start ,
substream - > dma_buffer . addr > > PAGE_SHIFT ,
area - > vm_end - area - > vm_start , area - > vm_page_prot ) ;
}
2013-10-24 16:25:32 +04:00
# endif /* CONFIG_GENERIC_ALLOCATOR */
2009-11-26 14:40:21 +03:00
# ifdef ARCH_HAS_DMA_MMAP_COHERENT
if ( ! substream - > ops - > page & &
substream - > dma_buffer . dev . type = = SNDRV_DMA_TYPE_DEV )
return dma_mmap_coherent ( substream - > dma_buffer . dev . dev ,
area ,
substream - > runtime - > dma_area ,
substream - > runtime - > dma_addr ,
area - > vm_end - area - > vm_start ) ;
2010-05-12 12:32:42 +04:00
# elif defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
if ( substream - > dma_buffer . dev . type = = SNDRV_DMA_TYPE_DEV & &
! plat_device_is_coherent ( substream - > dma_buffer . dev . dev ) )
area - > vm_page_prot = pgprot_noncached ( area - > vm_page_prot ) ;
2009-11-26 14:40:21 +03:00
# endif /* ARCH_HAS_DMA_MMAP_COHERENT */
/* mmap with fault handler */
area - > vm_ops = & snd_pcm_vm_ops_data_fault ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2011-09-28 19:12:59 +04:00
EXPORT_SYMBOL_GPL ( snd_pcm_lib_default_mmap ) ;
2005-04-17 02:20:36 +04:00
/*
* mmap the DMA buffer on I / O memory area
*/
# if SNDRV_PCM_INFO_MMAP_IOMEM
2005-11-17 15:59:38 +03:00
int snd_pcm_lib_mmap_iomem ( struct snd_pcm_substream * substream ,
struct vm_area_struct * area )
2005-04-17 02:20:36 +04:00
{
2013-04-19 21:01:04 +04:00
struct snd_pcm_runtime * runtime = substream - > runtime ; ;
2005-04-17 02:20:36 +04:00
area - > vm_page_prot = pgprot_noncached ( area - > vm_page_prot ) ;
2013-04-19 21:01:04 +04:00
return vm_iomap_memory ( area , runtime - > dma_addr , runtime - > dma_bytes ) ;
2005-04-17 02:20:36 +04:00
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_lib_mmap_iomem ) ;
2005-04-17 02:20:36 +04:00
# endif /* SNDRV_PCM_INFO_MMAP */
/*
* mmap DMA buffer
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_mmap_data ( struct snd_pcm_substream * substream , struct file * file ,
2005-04-17 02:20:36 +04:00
struct vm_area_struct * area )
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime ;
2005-04-17 02:20:36 +04:00
long size ;
unsigned long offset ;
size_t dma_bytes ;
2009-11-26 14:40:21 +03:00
int err ;
2005-04-17 02:20:36 +04:00
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
if ( ! ( area - > vm_flags & ( VM_WRITE | VM_READ ) ) )
return - EINVAL ;
} else {
if ( ! ( area - > vm_flags & VM_READ ) )
return - EINVAL ;
}
runtime = substream - > runtime ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN )
return - EBADFD ;
if ( ! ( runtime - > info & SNDRV_PCM_INFO_MMAP ) )
return - ENXIO ;
if ( runtime - > access = = SNDRV_PCM_ACCESS_RW_INTERLEAVED | |
runtime - > access = = SNDRV_PCM_ACCESS_RW_NONINTERLEAVED )
return - EINVAL ;
size = area - > vm_end - area - > vm_start ;
offset = area - > vm_pgoff < < PAGE_SHIFT ;
dma_bytes = PAGE_ALIGN ( runtime - > dma_bytes ) ;
if ( ( size_t ) size > dma_bytes )
return - EINVAL ;
if ( offset > dma_bytes - size )
return - EINVAL ;
2009-11-26 14:40:21 +03:00
area - > vm_ops = & snd_pcm_vm_ops_data ;
area - > vm_private_data = substream ;
2005-04-17 02:20:36 +04:00
if ( substream - > ops - > mmap )
2009-11-26 14:40:21 +03:00
err = substream - > ops - > mmap ( substream , area ) ;
2005-04-17 02:20:36 +04:00
else
2011-09-28 19:12:59 +04:00
err = snd_pcm_lib_default_mmap ( substream , area ) ;
2009-11-26 14:40:21 +03:00
if ( ! err )
atomic_inc ( & substream - > mmap_count ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_mmap_data ) ;
2005-04-17 02:20:36 +04:00
static int snd_pcm_mmap ( struct file * file , struct vm_area_struct * area )
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_file * pcm_file ;
struct snd_pcm_substream * substream ;
2005-04-17 02:20:36 +04:00
unsigned long offset ;
pcm_file = file - > private_data ;
substream = pcm_file - > substream ;
2008-08-08 19:09:09 +04:00
if ( PCM_RUNTIME_CHECK ( substream ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
offset = area - > vm_pgoff < < PAGE_SHIFT ;
switch ( offset ) {
case SNDRV_PCM_MMAP_OFFSET_STATUS :
2006-07-31 18:51:51 +04:00
if ( pcm_file - > no_compat_mmap )
2005-04-17 02:20:36 +04:00
return - ENXIO ;
return snd_pcm_mmap_status ( substream , file , area ) ;
case SNDRV_PCM_MMAP_OFFSET_CONTROL :
2006-07-31 18:51:51 +04:00
if ( pcm_file - > no_compat_mmap )
2005-04-17 02:20:36 +04:00
return - ENXIO ;
return snd_pcm_mmap_control ( substream , file , area ) ;
default :
return snd_pcm_mmap_data ( substream , file , area ) ;
}
return 0 ;
}
static int snd_pcm_fasync ( int fd , struct file * file , int on )
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_file * pcm_file ;
struct snd_pcm_substream * substream ;
struct snd_pcm_runtime * runtime ;
2005-04-17 02:20:36 +04:00
pcm_file = file - > private_data ;
substream = pcm_file - > substream ;
2008-08-08 19:09:09 +04:00
if ( PCM_RUNTIME_CHECK ( substream ) )
2010-04-07 20:29:46 +04:00
return - ENXIO ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
2010-04-07 20:29:46 +04:00
return fasync_helper ( fd , file , on , & runtime - > fasync ) ;
2005-04-17 02:20:36 +04:00
}
/*
* ioctl32 compat
*/
# ifdef CONFIG_COMPAT
# include "pcm_compat.c"
# else
# define snd_pcm_ioctl_compat NULL
# endif
/*
* To be removed helpers to keep binary compatibility
*/
2005-12-01 12:51:58 +03:00
# ifdef CONFIG_SND_SUPPORT_OLD_API
2005-04-17 02:20:36 +04:00
# define __OLD_TO_NEW_MASK(x) ((x&7)|((x&0x07fffff8)<<5))
# define __NEW_TO_OLD_MASK(x) ((x&7)|((x&0xffffff00)>>5))
2005-11-17 15:59:38 +03:00
static void snd_pcm_hw_convert_from_old_params ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_params_old * oparams )
2005-04-17 02:20:36 +04:00
{
unsigned int i ;
memset ( params , 0 , sizeof ( * params ) ) ;
params - > flags = oparams - > flags ;
for ( i = 0 ; i < ARRAY_SIZE ( oparams - > masks ) ; i + + )
params - > masks [ i ] . bits [ 0 ] = oparams - > masks [ i ] ;
memcpy ( params - > intervals , oparams - > intervals , sizeof ( oparams - > intervals ) ) ;
params - > rmask = __OLD_TO_NEW_MASK ( oparams - > rmask ) ;
params - > cmask = __OLD_TO_NEW_MASK ( oparams - > cmask ) ;
params - > info = oparams - > info ;
params - > msbits = oparams - > msbits ;
params - > rate_num = oparams - > rate_num ;
params - > rate_den = oparams - > rate_den ;
params - > fifo_size = oparams - > fifo_size ;
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_hw_convert_to_old_params ( struct snd_pcm_hw_params_old * oparams ,
struct snd_pcm_hw_params * params )
2005-04-17 02:20:36 +04:00
{
unsigned int i ;
memset ( oparams , 0 , sizeof ( * oparams ) ) ;
oparams - > flags = params - > flags ;
for ( i = 0 ; i < ARRAY_SIZE ( oparams - > masks ) ; i + + )
oparams - > masks [ i ] = params - > masks [ i ] . bits [ 0 ] ;
memcpy ( oparams - > intervals , params - > intervals , sizeof ( oparams - > intervals ) ) ;
oparams - > rmask = __NEW_TO_OLD_MASK ( params - > rmask ) ;
oparams - > cmask = __NEW_TO_OLD_MASK ( params - > cmask ) ;
oparams - > info = params - > info ;
oparams - > msbits = params - > msbits ;
oparams - > rate_num = params - > rate_num ;
oparams - > rate_den = params - > rate_den ;
oparams - > fifo_size = params - > fifo_size ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_refine_old_user ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params_old __user * _oparams )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_hw_params * params ;
struct snd_pcm_hw_params_old * oparams = NULL ;
2005-04-17 02:20:36 +04:00
int err ;
params = kmalloc ( sizeof ( * params ) , GFP_KERNEL ) ;
2009-04-10 05:43:08 +04:00
if ( ! params )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2009-04-10 05:43:08 +04:00
oparams = memdup_user ( _oparams , sizeof ( * oparams ) ) ;
if ( IS_ERR ( oparams ) ) {
err = PTR_ERR ( oparams ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
snd_pcm_hw_convert_from_old_params ( params , oparams ) ;
err = snd_pcm_hw_refine ( substream , params ) ;
snd_pcm_hw_convert_to_old_params ( oparams , params ) ;
if ( copy_to_user ( _oparams , oparams , sizeof ( * oparams ) ) ) {
if ( ! err )
err = - EFAULT ;
}
2009-04-10 05:43:08 +04:00
kfree ( oparams ) ;
2005-04-17 02:20:36 +04:00
out :
kfree ( params ) ;
return err ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_params_old_user ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params_old __user * _oparams )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_hw_params * params ;
struct snd_pcm_hw_params_old * oparams = NULL ;
2005-04-17 02:20:36 +04:00
int err ;
params = kmalloc ( sizeof ( * params ) , GFP_KERNEL ) ;
2009-04-10 05:43:08 +04:00
if ( ! params )
return - ENOMEM ;
oparams = memdup_user ( _oparams , sizeof ( * oparams ) ) ;
if ( IS_ERR ( oparams ) ) {
err = PTR_ERR ( oparams ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
snd_pcm_hw_convert_from_old_params ( params , oparams ) ;
err = snd_pcm_hw_params ( substream , params ) ;
snd_pcm_hw_convert_to_old_params ( oparams , params ) ;
if ( copy_to_user ( _oparams , oparams , sizeof ( * oparams ) ) ) {
if ( ! err )
err = - EFAULT ;
}
2009-04-10 05:43:08 +04:00
kfree ( oparams ) ;
2005-04-17 02:20:36 +04:00
out :
kfree ( params ) ;
return err ;
}
2005-12-01 12:51:58 +03:00
# endif /* CONFIG_SND_SUPPORT_OLD_API */
2005-04-17 02:20:36 +04:00
2008-09-03 12:54:36 +04:00
# ifndef CONFIG_MMU
2010-03-09 20:57:52 +03:00
static unsigned long snd_pcm_get_unmapped_area ( struct file * file ,
unsigned long addr ,
unsigned long len ,
unsigned long pgoff ,
unsigned long flags )
{
struct snd_pcm_file * pcm_file = file - > private_data ;
struct snd_pcm_substream * substream = pcm_file - > substream ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
unsigned long offset = pgoff < < PAGE_SHIFT ;
switch ( offset ) {
case SNDRV_PCM_MMAP_OFFSET_STATUS :
return ( unsigned long ) runtime - > status ;
case SNDRV_PCM_MMAP_OFFSET_CONTROL :
return ( unsigned long ) runtime - > control ;
default :
return ( unsigned long ) runtime - > dma_area + offset ;
}
2008-09-03 12:54:36 +04:00
}
# else
2010-03-09 20:57:52 +03:00
# define snd_pcm_get_unmapped_area NULL
2008-09-03 12:54:36 +04:00
# endif
2005-04-17 02:20:36 +04:00
/*
* Register section
*/
2007-02-12 11:55:37 +03:00
const struct file_operations snd_pcm_f_ops [ 2 ] = {
2005-04-17 02:20:36 +04:00
{
2005-11-20 16:03:48 +03:00
. owner = THIS_MODULE ,
. write = snd_pcm_write ,
2006-10-01 10:28:47 +04:00
. aio_write = snd_pcm_aio_write ,
2005-11-20 16:06:59 +03:00
. open = snd_pcm_playback_open ,
2005-11-20 16:03:48 +03:00
. release = snd_pcm_release ,
2010-04-13 13:49:04 +04:00
. llseek = no_llseek ,
2005-11-20 16:03:48 +03:00
. poll = snd_pcm_playback_poll ,
. unlocked_ioctl = snd_pcm_playback_ioctl ,
. compat_ioctl = snd_pcm_ioctl_compat ,
. mmap = snd_pcm_mmap ,
. fasync = snd_pcm_fasync ,
2010-03-09 20:57:52 +03:00
. get_unmapped_area = snd_pcm_get_unmapped_area ,
2005-04-17 02:20:36 +04:00
} ,
{
2005-11-20 16:03:48 +03:00
. owner = THIS_MODULE ,
. read = snd_pcm_read ,
2006-10-01 10:28:47 +04:00
. aio_read = snd_pcm_aio_read ,
2005-11-20 16:06:59 +03:00
. open = snd_pcm_capture_open ,
2005-11-20 16:03:48 +03:00
. release = snd_pcm_release ,
2010-04-13 13:49:04 +04:00
. llseek = no_llseek ,
2005-11-20 16:03:48 +03:00
. poll = snd_pcm_capture_poll ,
. unlocked_ioctl = snd_pcm_capture_ioctl ,
. compat_ioctl = snd_pcm_ioctl_compat ,
. mmap = snd_pcm_mmap ,
. fasync = snd_pcm_fasync ,
2010-03-09 20:57:52 +03:00
. get_unmapped_area = snd_pcm_get_unmapped_area ,
2005-04-17 02:20:36 +04:00
}
} ;