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
* Abramo Bagnara < abramo @ alsa - project . org >
*
*
* 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/slab.h>
# include <linux/time.h>
# 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>
/*
* fill ring buffer with silence
* runtime - > silence_start : starting pointer to silence area
* runtime - > silence_filled : size filled with silence
* runtime - > silence_threshold : threshold from application
* runtime - > silence_size : maximal size from application
*
* when runtime - > silence_size > = runtime - > boundary - fill processed area with silence immediately
*/
2005-11-17 15:59:38 +03:00
void snd_pcm_playback_silence ( struct snd_pcm_substream * substream , snd_pcm_uframes_t new_hw_ptr )
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_uframes_t frames , ofs , transfer ;
if ( runtime - > silence_size < runtime - > boundary ) {
snd_pcm_sframes_t noise_dist , n ;
if ( runtime - > silence_start ! = runtime - > control - > appl_ptr ) {
n = runtime - > control - > appl_ptr - runtime - > silence_start ;
if ( n < 0 )
n + = runtime - > boundary ;
if ( ( snd_pcm_uframes_t ) n < runtime - > silence_filled )
runtime - > silence_filled - = n ;
else
runtime - > silence_filled = 0 ;
runtime - > silence_start = runtime - > control - > appl_ptr ;
}
2005-12-07 17:28:07 +03:00
if ( runtime - > silence_filled > = runtime - > buffer_size )
2005-04-17 02:20:36 +04:00
return ;
noise_dist = snd_pcm_playback_hw_avail ( runtime ) + runtime - > silence_filled ;
if ( noise_dist > = ( snd_pcm_sframes_t ) runtime - > silence_threshold )
return ;
frames = runtime - > silence_threshold - noise_dist ;
if ( frames > runtime - > silence_size )
frames = runtime - > silence_size ;
} else {
if ( new_hw_ptr = = ULONG_MAX ) { /* initialization */
snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail ( runtime ) ;
runtime - > silence_filled = avail > 0 ? avail : 0 ;
runtime - > silence_start = ( runtime - > status - > hw_ptr +
runtime - > silence_filled ) %
runtime - > boundary ;
} else {
ofs = runtime - > status - > hw_ptr ;
frames = new_hw_ptr - ofs ;
if ( ( snd_pcm_sframes_t ) frames < 0 )
frames + = runtime - > boundary ;
runtime - > silence_filled - = frames ;
if ( ( snd_pcm_sframes_t ) runtime - > silence_filled < 0 ) {
runtime - > silence_filled = 0 ;
2006-10-23 18:26:57 +04:00
runtime - > silence_start = new_hw_ptr ;
2005-04-17 02:20:36 +04:00
} else {
2006-10-23 18:26:57 +04:00
runtime - > silence_start = ofs ;
2005-04-17 02:20:36 +04:00
}
}
frames = runtime - > buffer_size - runtime - > silence_filled ;
}
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( frames > runtime - > buffer_size ) )
return ;
2005-04-17 02:20:36 +04:00
if ( frames = = 0 )
return ;
2006-10-23 18:26:57 +04:00
ofs = runtime - > silence_start % runtime - > buffer_size ;
2005-04-17 02:20:36 +04:00
while ( frames > 0 ) {
transfer = ofs + frames > runtime - > buffer_size ? runtime - > buffer_size - ofs : frames ;
if ( runtime - > access = = SNDRV_PCM_ACCESS_RW_INTERLEAVED | |
runtime - > access = = SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ) {
if ( substream - > ops - > silence ) {
int err ;
err = substream - > ops - > silence ( substream , - 1 , ofs , transfer ) ;
2008-08-08 19:09:09 +04:00
snd_BUG_ON ( err < 0 ) ;
2005-04-17 02:20:36 +04:00
} else {
char * hwbuf = runtime - > dma_area + frames_to_bytes ( runtime , ofs ) ;
snd_pcm_format_set_silence ( runtime - > format , hwbuf , transfer * runtime - > channels ) ;
}
} else {
unsigned int c ;
unsigned int channels = runtime - > channels ;
if ( substream - > ops - > silence ) {
for ( c = 0 ; c < channels ; + + c ) {
int err ;
err = substream - > ops - > silence ( substream , c , ofs , transfer ) ;
2008-08-08 19:09:09 +04:00
snd_BUG_ON ( err < 0 ) ;
2005-04-17 02:20:36 +04:00
}
} else {
size_t dma_csize = runtime - > dma_bytes / channels ;
for ( c = 0 ; c < channels ; + + c ) {
char * hwbuf = runtime - > dma_area + ( c * dma_csize ) + samples_to_bytes ( runtime , ofs ) ;
snd_pcm_format_set_silence ( runtime - > format , hwbuf , transfer ) ;
}
}
}
runtime - > silence_filled + = transfer ;
frames - = transfer ;
ofs = 0 ;
}
}
2009-03-03 19:00:15 +03:00
# ifdef CONFIG_SND_PCM_XRUN_DEBUG
# define xrun_debug(substream) ((substream)->pstr->xrun_debug)
# else
# define xrun_debug(substream) 0
# endif
# define dump_stack_on_xrun(substream) do { \
if ( xrun_debug ( substream ) > 1 ) \
dump_stack ( ) ; \
} while ( 0 )
2005-11-17 15:59:38 +03:00
static void xrun ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
snd_pcm_stop ( substream , SNDRV_PCM_STATE_XRUN ) ;
2009-03-03 19:00:15 +03:00
if ( xrun_debug ( substream ) ) {
2005-04-17 02:20:36 +04:00
snd_printd ( KERN_DEBUG " XRUN: pcmC%dD%d%c \n " ,
substream - > pcm - > card - > number ,
substream - > pcm - > device ,
substream - > stream ? ' c ' : ' p ' ) ;
2009-03-03 19:00:15 +03:00
dump_stack_on_xrun ( substream ) ;
2005-04-17 02:20:36 +04:00
}
}
2009-03-19 11:59:21 +03:00
static snd_pcm_uframes_t
snd_pcm_update_hw_ptr_pos ( struct snd_pcm_substream * substream ,
struct snd_pcm_runtime * runtime )
2005-04-17 02:20:36 +04:00
{
snd_pcm_uframes_t pos ;
2008-01-11 10:45:08 +03:00
if ( runtime - > tstamp_mode = = SNDRV_PCM_TSTAMP_ENABLE )
snd_pcm_gettime ( runtime , ( struct timespec * ) & runtime - > status - > tstamp ) ;
2005-04-17 02:20:36 +04:00
pos = substream - > ops - > pointer ( substream ) ;
if ( pos = = SNDRV_PCM_POS_XRUN )
return pos ; /* XRUN */
if ( pos > = runtime - > buffer_size ) {
2009-03-19 12:01:47 +03:00
if ( printk_ratelimit ( ) ) {
snd_printd ( KERN_ERR " BUG: stream = %i, pos = 0x%lx, "
" buffer size = 0x%lx, period size = 0x%lx \n " ,
substream - > stream , pos , runtime - > buffer_size ,
runtime - > period_size ) ;
}
pos = 0 ;
2005-10-10 13:46:31 +04:00
}
2005-04-17 02:20:36 +04:00
pos - = pos % runtime - > min_align ;
return pos ;
}
2009-03-19 11:59:21 +03:00
static int snd_pcm_update_hw_ptr_post ( struct snd_pcm_substream * substream ,
struct snd_pcm_runtime * runtime )
2005-04-17 02:20:36 +04:00
{
snd_pcm_uframes_t avail ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
avail = snd_pcm_playback_avail ( runtime ) ;
else
avail = snd_pcm_capture_avail ( runtime ) ;
if ( avail > runtime - > avail_max )
runtime - > avail_max = avail ;
if ( avail > = runtime - > stop_threshold ) {
if ( substream - > runtime - > status - > state = = SNDRV_PCM_STATE_DRAINING )
snd_pcm_drain_done ( substream ) ;
else
xrun ( substream ) ;
return - EPIPE ;
}
if ( avail > = runtime - > control - > avail_min )
wake_up ( & runtime - > sleep ) ;
return 0 ;
}
2009-03-03 19:00:15 +03:00
# define hw_ptr_error(substream, fmt, args...) \
do { \
if ( xrun_debug ( substream ) ) { \
if ( printk_ratelimit ( ) ) { \
2009-03-19 11:55:15 +03:00
snd_printd ( " PCM: " fmt , # # args ) ; \
2009-03-03 19:00:15 +03:00
} \
dump_stack_on_xrun ( substream ) ; \
} \
} while ( 0 )
2009-03-19 11:59:21 +03:00
static int snd_pcm_update_hw_ptr_interrupt ( 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
snd_pcm_uframes_t pos ;
2009-04-10 14:28:58 +04:00
snd_pcm_uframes_t old_hw_ptr , new_hw_ptr , hw_ptr_interrupt , hw_base ;
snd_pcm_sframes_t hdelta , delta ;
unsigned long jdelta ;
2005-04-17 02:20:36 +04:00
2009-04-10 14:28:58 +04:00
old_hw_ptr = runtime - > status - > hw_ptr ;
2005-04-17 02:20:36 +04:00
pos = snd_pcm_update_hw_ptr_pos ( substream , runtime ) ;
if ( pos = = SNDRV_PCM_POS_XRUN ) {
xrun ( substream ) ;
return - EPIPE ;
}
2009-03-03 19:00:15 +03:00
hw_base = runtime - > hw_ptr_base ;
new_hw_ptr = hw_base + pos ;
2005-04-17 02:20:36 +04:00
hw_ptr_interrupt = runtime - > hw_ptr_interrupt + runtime - > period_size ;
2009-03-03 19:00:15 +03:00
delta = new_hw_ptr - hw_ptr_interrupt ;
2009-03-19 12:08:49 +03:00
if ( hw_ptr_interrupt > = runtime - > boundary ) {
2009-03-20 18:26:15 +03:00
hw_ptr_interrupt - = runtime - > boundary ;
if ( hw_base < runtime - > boundary / 2 )
/* hw_base was already lapped; recalc delta */
2009-03-19 12:08:49 +03:00
delta = new_hw_ptr - hw_ptr_interrupt ;
}
2009-03-03 19:00:15 +03:00
if ( delta < 0 ) {
delta + = runtime - > buffer_size ;
if ( delta < 0 ) {
hw_ptr_error ( substream ,
" Unexpected hw_pointer value "
" (stream=%i, pos=%ld, intr_ptr=%ld) \n " ,
substream - > stream , ( long ) pos ,
( long ) hw_ptr_interrupt ) ;
/* rebase to interrupt position */
hw_base = new_hw_ptr = hw_ptr_interrupt ;
2009-03-19 12:08:49 +03:00
/* align hw_base to buffer_size */
hw_base - = hw_base % runtime - > buffer_size ;
2009-03-03 19:00:15 +03:00
delta = 0 ;
} else {
hw_base + = runtime - > buffer_size ;
2009-03-20 18:26:15 +03:00
if ( hw_base > = runtime - > boundary )
2009-03-03 19:00:15 +03:00
hw_base = 0 ;
new_hw_ptr = hw_base + pos ;
2005-04-17 02:20:36 +04:00
}
}
2009-04-10 14:28:58 +04:00
hdelta = new_hw_ptr - old_hw_ptr ;
jdelta = jiffies - runtime - > hw_ptr_jiffies ;
if ( ( ( hdelta * HZ ) / runtime - > rate ) > jdelta + HZ / 100 ) {
delta = jdelta /
( ( ( runtime - > period_size * HZ ) / runtime - > rate )
+ HZ / 100 ) ;
hw_ptr_error ( substream ,
" hw_ptr skipping! [Q] "
" (pos=%ld, delta=%ld, period=%ld, "
" jdelta=%lu/%lu/%lu) \n " ,
( long ) pos , ( long ) hdelta ,
( long ) runtime - > period_size , jdelta ,
( ( hdelta * HZ ) / runtime - > rate ) , delta ) ;
hw_ptr_interrupt = runtime - > hw_ptr_interrupt +
runtime - > period_size * delta ;
if ( hw_ptr_interrupt > = runtime - > boundary )
hw_ptr_interrupt - = runtime - > boundary ;
/* rebase to interrupt position */
hw_base = new_hw_ptr = hw_ptr_interrupt ;
/* align hw_base to buffer_size */
hw_base - = hw_base % runtime - > buffer_size ;
delta = 0 ;
}
if ( delta > runtime - > period_size + runtime - > period_size / 2 ) {
2009-03-03 19:00:15 +03:00
hw_ptr_error ( substream ,
" Lost interrupts? "
" (stream=%i, delta=%ld, intr_ptr=%ld) \n " ,
substream - > stream , ( long ) delta ,
( long ) hw_ptr_interrupt ) ;
/* rebase hw_ptr_interrupt */
hw_ptr_interrupt =
new_hw_ptr - new_hw_ptr % runtime - > period_size ;
}
2005-04-17 02:20:36 +04:00
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK & &
runtime - > silence_size > 0 )
snd_pcm_playback_silence ( substream , new_hw_ptr ) ;
2009-03-03 19:00:15 +03:00
runtime - > hw_ptr_base = hw_base ;
2005-04-17 02:20:36 +04:00
runtime - > status - > hw_ptr = new_hw_ptr ;
2009-04-10 14:28:58 +04:00
runtime - > hw_ptr_jiffies = jiffies ;
2009-03-03 19:00:15 +03:00
runtime - > hw_ptr_interrupt = hw_ptr_interrupt ;
2005-04-17 02:20:36 +04:00
return snd_pcm_update_hw_ptr_post ( substream , runtime ) ;
}
/* CAUTION: call it with irq disabled */
2005-11-17 15:59:38 +03:00
int snd_pcm_update_hw_ptr ( 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
snd_pcm_uframes_t pos ;
2009-03-03 19:00:15 +03:00
snd_pcm_uframes_t old_hw_ptr , new_hw_ptr , hw_base ;
2005-04-17 02:20:36 +04:00
snd_pcm_sframes_t delta ;
2009-04-10 14:28:58 +04:00
unsigned long jdelta ;
2005-04-17 02:20:36 +04:00
old_hw_ptr = runtime - > status - > hw_ptr ;
pos = snd_pcm_update_hw_ptr_pos ( substream , runtime ) ;
if ( pos = = SNDRV_PCM_POS_XRUN ) {
xrun ( substream ) ;
return - EPIPE ;
}
2009-03-03 19:00:15 +03:00
hw_base = runtime - > hw_ptr_base ;
new_hw_ptr = hw_base + pos ;
2005-04-17 02:20:36 +04:00
2009-03-03 19:00:15 +03:00
delta = new_hw_ptr - old_hw_ptr ;
2009-04-10 14:28:58 +04:00
jdelta = jiffies - runtime - > hw_ptr_jiffies ;
2009-03-03 19:00:15 +03:00
if ( delta < 0 ) {
delta + = runtime - > buffer_size ;
if ( delta < 0 ) {
hw_ptr_error ( substream ,
" Unexpected hw_pointer value [2] "
2009-04-10 14:28:58 +04:00
" (stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li) \n " ,
2009-03-03 19:00:15 +03:00
substream - > stream , ( long ) pos ,
2009-04-10 14:28:58 +04:00
( long ) old_hw_ptr , jdelta ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-03-03 19:00:15 +03:00
hw_base + = runtime - > buffer_size ;
2009-03-20 18:26:15 +03:00
if ( hw_base > = runtime - > boundary )
2009-03-03 19:00:15 +03:00
hw_base = 0 ;
new_hw_ptr = hw_base + pos ;
}
2009-04-10 14:28:58 +04:00
if ( ( ( delta * HZ ) / runtime - > rate ) > jdelta + HZ / 100 ) {
2009-03-03 19:00:15 +03:00
hw_ptr_error ( substream ,
" hw_ptr skipping! "
2009-04-10 14:28:58 +04:00
" (pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu) \n " ,
2009-03-03 19:00:15 +03:00
( long ) pos , ( long ) delta ,
2009-04-10 14:28:58 +04:00
( long ) runtime - > period_size , jdelta ,
( ( delta * HZ ) / runtime - > rate ) ) ;
2009-03-03 19:00:15 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK & &
runtime - > silence_size > 0 )
snd_pcm_playback_silence ( substream , new_hw_ptr ) ;
2009-03-03 19:00:15 +03:00
runtime - > hw_ptr_base = hw_base ;
2005-04-17 02:20:36 +04:00
runtime - > status - > hw_ptr = new_hw_ptr ;
2009-04-10 14:28:58 +04:00
runtime - > hw_ptr_jiffies = jiffies ;
2005-04-17 02:20:36 +04:00
return snd_pcm_update_hw_ptr_post ( substream , runtime ) ;
}
/**
* snd_pcm_set_ops - set the PCM operators
* @ pcm : the pcm instance
* @ direction : stream direction , SNDRV_PCM_STREAM_XXX
* @ ops : the operator table
*
* Sets the given PCM operators to the pcm instance .
*/
2005-11-17 15:59:38 +03:00
void snd_pcm_set_ops ( struct snd_pcm * pcm , int direction , struct snd_pcm_ops * ops )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_str * stream = & pcm - > streams [ direction ] ;
struct snd_pcm_substream * substream ;
2005-04-17 02:20:36 +04:00
for ( substream = stream - > substream ; substream ! = NULL ; substream = substream - > next )
substream - > ops = ops ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_set_ops ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_pcm_sync - set the PCM sync id
* @ substream : the pcm substream
*
* Sets the PCM sync identifier for the card .
*/
2005-11-17 15:59:38 +03:00
void snd_pcm_set_sync ( 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
runtime - > sync . id32 [ 0 ] = substream - > pcm - > card - > number ;
runtime - > sync . id32 [ 1 ] = - 1 ;
runtime - > sync . id32 [ 2 ] = - 1 ;
runtime - > sync . id32 [ 3 ] = - 1 ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_set_sync ) ;
2005-04-17 02:20:36 +04:00
/*
* Standard ioctl routine
*/
static inline unsigned int div32 ( unsigned int a , unsigned int b ,
unsigned int * r )
{
if ( b = = 0 ) {
* r = 0 ;
return UINT_MAX ;
}
* r = a % b ;
return a / b ;
}
static inline unsigned int div_down ( unsigned int a , unsigned int b )
{
if ( b = = 0 )
return UINT_MAX ;
return a / b ;
}
static inline unsigned int div_up ( unsigned int a , unsigned int b )
{
unsigned int r ;
unsigned int q ;
if ( b = = 0 )
return UINT_MAX ;
q = div32 ( a , b , & r ) ;
if ( r )
+ + q ;
return q ;
}
static inline unsigned int mul ( unsigned int a , unsigned int b )
{
if ( a = = 0 )
return 0 ;
if ( div_down ( UINT_MAX , a ) < b )
return UINT_MAX ;
return a * b ;
}
static inline unsigned int muldiv32 ( unsigned int a , unsigned int b ,
unsigned int c , unsigned int * r )
{
u_int64_t n = ( u_int64_t ) a * b ;
if ( c = = 0 ) {
2008-08-08 19:09:09 +04:00
snd_BUG_ON ( ! n ) ;
2005-04-17 02:20:36 +04:00
* r = 0 ;
return UINT_MAX ;
}
div64_32 ( & n , c , r ) ;
if ( n > = UINT_MAX ) {
* r = 0 ;
return UINT_MAX ;
}
return n ;
}
/**
* snd_interval_refine - refine the interval value of configurator
* @ i : the interval value to refine
* @ v : the interval value to refer to
*
* Refines the interval value with the reference value .
* The interval is changed to the range satisfying both intervals .
* The interval status ( min , max , integer , etc . ) are evaluated .
*
* Returns non - zero if the value is changed , zero if not changed .
*/
2005-11-17 15:59:38 +03:00
int snd_interval_refine ( struct snd_interval * i , const struct snd_interval * v )
2005-04-17 02:20:36 +04:00
{
int changed = 0 ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( snd_interval_empty ( i ) ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( i - > min < v - > min ) {
i - > min = v - > min ;
i - > openmin = v - > openmin ;
changed = 1 ;
} else if ( i - > min = = v - > min & & ! i - > openmin & & v - > openmin ) {
i - > openmin = 1 ;
changed = 1 ;
}
if ( i - > max > v - > max ) {
i - > max = v - > max ;
i - > openmax = v - > openmax ;
changed = 1 ;
} else if ( i - > max = = v - > max & & ! i - > openmax & & v - > openmax ) {
i - > openmax = 1 ;
changed = 1 ;
}
if ( ! i - > integer & & v - > integer ) {
i - > integer = 1 ;
changed = 1 ;
}
if ( i - > integer ) {
if ( i - > openmin ) {
i - > min + + ;
i - > openmin = 0 ;
}
if ( i - > openmax ) {
i - > max - - ;
i - > openmax = 0 ;
}
} else if ( ! i - > openmin & & ! i - > openmax & & i - > min = = i - > max )
i - > integer = 1 ;
if ( snd_interval_checkempty ( i ) ) {
snd_interval_none ( i ) ;
return - EINVAL ;
}
return changed ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_interval_refine ) ;
2005-11-17 15:59:38 +03:00
static int snd_interval_refine_first ( struct snd_interval * i )
2005-04-17 02:20:36 +04:00
{
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( snd_interval_empty ( i ) ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( snd_interval_single ( i ) )
return 0 ;
i - > max = i - > min ;
i - > openmax = i - > openmin ;
if ( i - > openmax )
i - > max + + ;
return 1 ;
}
2005-11-17 15:59:38 +03:00
static int snd_interval_refine_last ( struct snd_interval * i )
2005-04-17 02:20:36 +04:00
{
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( snd_interval_empty ( i ) ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( snd_interval_single ( i ) )
return 0 ;
i - > min = i - > max ;
i - > openmin = i - > openmax ;
if ( i - > openmin )
i - > min - - ;
return 1 ;
}
2005-11-17 15:59:38 +03:00
void snd_interval_mul ( const struct snd_interval * a , const struct snd_interval * b , struct snd_interval * c )
2005-04-17 02:20:36 +04:00
{
if ( a - > empty | | b - > empty ) {
snd_interval_none ( c ) ;
return ;
}
c - > empty = 0 ;
c - > min = mul ( a - > min , b - > min ) ;
c - > openmin = ( a - > openmin | | b - > openmin ) ;
c - > max = mul ( a - > max , b - > max ) ;
c - > openmax = ( a - > openmax | | b - > openmax ) ;
c - > integer = ( a - > integer & & b - > integer ) ;
}
/**
* snd_interval_div - refine the interval value with division
2005-09-07 15:38:19 +04:00
* @ a : dividend
* @ b : divisor
* @ c : quotient
2005-04-17 02:20:36 +04:00
*
* c = a / b
*
* Returns non - zero if the value is changed , zero if not changed .
*/
2005-11-17 15:59:38 +03:00
void snd_interval_div ( const struct snd_interval * a , const struct snd_interval * b , struct snd_interval * c )
2005-04-17 02:20:36 +04:00
{
unsigned int r ;
if ( a - > empty | | b - > empty ) {
snd_interval_none ( c ) ;
return ;
}
c - > empty = 0 ;
c - > min = div32 ( a - > min , b - > max , & r ) ;
c - > openmin = ( r | | a - > openmin | | b - > openmax ) ;
if ( b - > min > 0 ) {
c - > max = div32 ( a - > max , b - > min , & r ) ;
if ( r ) {
c - > max + + ;
c - > openmax = 1 ;
} else
c - > openmax = ( a - > openmax | | b - > openmin ) ;
} else {
c - > max = UINT_MAX ;
c - > openmax = 0 ;
}
c - > integer = 0 ;
}
/**
* snd_interval_muldivk - refine the interval value
2005-09-07 15:38:19 +04:00
* @ a : dividend 1
* @ b : dividend 2
* @ k : divisor ( as integer )
* @ c : result
*
2005-04-17 02:20:36 +04:00
* c = a * b / k
*
* Returns non - zero if the value is changed , zero if not changed .
*/
2005-11-17 15:59:38 +03:00
void snd_interval_muldivk ( const struct snd_interval * a , const struct snd_interval * b ,
unsigned int k , struct snd_interval * c )
2005-04-17 02:20:36 +04:00
{
unsigned int r ;
if ( a - > empty | | b - > empty ) {
snd_interval_none ( c ) ;
return ;
}
c - > empty = 0 ;
c - > min = muldiv32 ( a - > min , b - > min , k , & r ) ;
c - > openmin = ( r | | a - > openmin | | b - > openmin ) ;
c - > max = muldiv32 ( a - > max , b - > max , k , & r ) ;
if ( r ) {
c - > max + + ;
c - > openmax = 1 ;
} else
c - > openmax = ( a - > openmax | | b - > openmax ) ;
c - > integer = 0 ;
}
/**
* snd_interval_mulkdiv - refine the interval value
2005-09-07 15:38:19 +04:00
* @ a : dividend 1
* @ k : dividend 2 ( as integer )
* @ b : divisor
* @ c : result
2005-04-17 02:20:36 +04:00
*
* c = a * k / b
*
* Returns non - zero if the value is changed , zero if not changed .
*/
2005-11-17 15:59:38 +03:00
void snd_interval_mulkdiv ( const struct snd_interval * a , unsigned int k ,
const struct snd_interval * b , struct snd_interval * c )
2005-04-17 02:20:36 +04:00
{
unsigned int r ;
if ( a - > empty | | b - > empty ) {
snd_interval_none ( c ) ;
return ;
}
c - > empty = 0 ;
c - > min = muldiv32 ( a - > min , k , b - > max , & r ) ;
c - > openmin = ( r | | a - > openmin | | b - > openmax ) ;
if ( b - > min > 0 ) {
c - > max = muldiv32 ( a - > max , k , b - > min , & r ) ;
if ( r ) {
c - > max + + ;
c - > openmax = 1 ;
} else
c - > openmax = ( a - > openmax | | b - > openmin ) ;
} else {
c - > max = UINT_MAX ;
c - > openmax = 0 ;
}
c - > integer = 0 ;
}
/* ---- */
/**
* snd_interval_ratnum - refine the interval value
2005-09-07 15:38:19 +04:00
* @ i : interval to refine
* @ rats_count : number of ratnum_t
* @ rats : ratnum_t array
* @ nump : pointer to store the resultant numerator
* @ denp : pointer to store the resultant denominator
2005-04-17 02:20:36 +04:00
*
* Returns non - zero if the value is changed , zero if not changed .
*/
2005-11-17 15:59:38 +03:00
int snd_interval_ratnum ( struct snd_interval * i ,
unsigned int rats_count , struct snd_ratnum * rats ,
unsigned int * nump , unsigned int * denp )
2005-04-17 02:20:36 +04:00
{
unsigned int best_num , best_diff , best_den ;
unsigned int k ;
2005-11-17 15:59:38 +03:00
struct snd_interval t ;
2005-04-17 02:20:36 +04:00
int err ;
best_num = best_den = best_diff = 0 ;
for ( k = 0 ; k < rats_count ; + + k ) {
unsigned int num = rats [ k ] . num ;
unsigned int den ;
unsigned int q = i - > min ;
int diff ;
if ( q = = 0 )
q = 1 ;
den = div_down ( num , q ) ;
if ( den < rats [ k ] . den_min )
continue ;
if ( den > rats [ k ] . den_max )
den = rats [ k ] . den_max ;
else {
unsigned int r ;
r = ( den - rats [ k ] . den_min ) % rats [ k ] . den_step ;
if ( r ! = 0 )
den - = r ;
}
diff = num - q * den ;
if ( best_num = = 0 | |
diff * best_den < best_diff * den ) {
best_diff = diff ;
best_den = den ;
best_num = num ;
}
}
if ( best_den = = 0 ) {
i - > empty = 1 ;
return - EINVAL ;
}
t . min = div_down ( best_num , best_den ) ;
t . openmin = ! ! ( best_num % best_den ) ;
best_num = best_den = best_diff = 0 ;
for ( k = 0 ; k < rats_count ; + + k ) {
unsigned int num = rats [ k ] . num ;
unsigned int den ;
unsigned int q = i - > max ;
int diff ;
if ( q = = 0 ) {
i - > empty = 1 ;
return - EINVAL ;
}
den = div_up ( num , q ) ;
if ( den > rats [ k ] . den_max )
continue ;
if ( den < rats [ k ] . den_min )
den = rats [ k ] . den_min ;
else {
unsigned int r ;
r = ( den - rats [ k ] . den_min ) % rats [ k ] . den_step ;
if ( r ! = 0 )
den + = rats [ k ] . den_step - r ;
}
diff = q * den - num ;
if ( best_num = = 0 | |
diff * best_den < best_diff * den ) {
best_diff = diff ;
best_den = den ;
best_num = num ;
}
}
if ( best_den = = 0 ) {
i - > empty = 1 ;
return - EINVAL ;
}
t . max = div_up ( best_num , best_den ) ;
t . openmax = ! ! ( best_num % best_den ) ;
t . integer = 0 ;
err = snd_interval_refine ( i , & t ) ;
if ( err < 0 )
return err ;
if ( snd_interval_single ( i ) ) {
if ( nump )
* nump = best_num ;
if ( denp )
* denp = best_den ;
}
return err ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_interval_ratnum ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_interval_ratden - refine the interval value
2005-09-07 15:38:19 +04:00
* @ i : interval to refine
2005-11-17 15:59:38 +03:00
* @ rats_count : number of struct ratden
* @ rats : struct ratden array
2005-09-07 15:38:19 +04:00
* @ nump : pointer to store the resultant numerator
* @ denp : pointer to store the resultant denominator
2005-04-17 02:20:36 +04:00
*
* Returns non - zero if the value is changed , zero if not changed .
*/
2005-11-17 15:59:38 +03:00
static int snd_interval_ratden ( struct snd_interval * i ,
unsigned int rats_count , struct snd_ratden * rats ,
2005-04-17 02:20:36 +04:00
unsigned int * nump , unsigned int * denp )
{
unsigned int best_num , best_diff , best_den ;
unsigned int k ;
2005-11-17 15:59:38 +03:00
struct snd_interval t ;
2005-04-17 02:20:36 +04:00
int err ;
best_num = best_den = best_diff = 0 ;
for ( k = 0 ; k < rats_count ; + + k ) {
unsigned int num ;
unsigned int den = rats [ k ] . den ;
unsigned int q = i - > min ;
int diff ;
num = mul ( q , den ) ;
if ( num > rats [ k ] . num_max )
continue ;
if ( num < rats [ k ] . num_min )
num = rats [ k ] . num_max ;
else {
unsigned int r ;
r = ( num - rats [ k ] . num_min ) % rats [ k ] . num_step ;
if ( r ! = 0 )
num + = rats [ k ] . num_step - r ;
}
diff = num - q * den ;
if ( best_num = = 0 | |
diff * best_den < best_diff * den ) {
best_diff = diff ;
best_den = den ;
best_num = num ;
}
}
if ( best_den = = 0 ) {
i - > empty = 1 ;
return - EINVAL ;
}
t . min = div_down ( best_num , best_den ) ;
t . openmin = ! ! ( best_num % best_den ) ;
best_num = best_den = best_diff = 0 ;
for ( k = 0 ; k < rats_count ; + + k ) {
unsigned int num ;
unsigned int den = rats [ k ] . den ;
unsigned int q = i - > max ;
int diff ;
num = mul ( q , den ) ;
if ( num < rats [ k ] . num_min )
continue ;
if ( num > rats [ k ] . num_max )
num = rats [ k ] . num_max ;
else {
unsigned int r ;
r = ( num - rats [ k ] . num_min ) % rats [ k ] . num_step ;
if ( r ! = 0 )
num - = r ;
}
diff = q * den - num ;
if ( best_num = = 0 | |
diff * best_den < best_diff * den ) {
best_diff = diff ;
best_den = den ;
best_num = num ;
}
}
if ( best_den = = 0 ) {
i - > empty = 1 ;
return - EINVAL ;
}
t . max = div_up ( best_num , best_den ) ;
t . openmax = ! ! ( best_num % best_den ) ;
t . integer = 0 ;
err = snd_interval_refine ( i , & t ) ;
if ( err < 0 )
return err ;
if ( snd_interval_single ( i ) ) {
if ( nump )
* nump = best_num ;
if ( denp )
* denp = best_den ;
}
return err ;
}
/**
* snd_interval_list - refine the interval value from the list
* @ i : the interval value to refine
* @ count : the number of elements in the list
* @ list : the value list
* @ mask : the bit - mask to evaluate
*
* Refines the interval value from the list .
* When mask is non - zero , only the elements corresponding to bit 1 are
* evaluated .
*
* Returns non - zero if the value is changed , zero if not changed .
*/
2005-11-17 15:59:38 +03:00
int snd_interval_list ( struct snd_interval * i , unsigned int count , unsigned int * list , unsigned int mask )
2005-04-17 02:20:36 +04:00
{
unsigned int k ;
int changed = 0 ;
2007-02-01 16:53:49 +03:00
if ( ! count ) {
i - > empty = 1 ;
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
for ( k = 0 ; k < count ; k + + ) {
if ( mask & & ! ( mask & ( 1 < < k ) ) )
continue ;
if ( i - > min = = list [ k ] & & ! i - > openmin )
goto _l1 ;
if ( i - > min < list [ k ] ) {
i - > min = list [ k ] ;
i - > openmin = 0 ;
changed = 1 ;
goto _l1 ;
}
}
i - > empty = 1 ;
return - EINVAL ;
_l1 :
for ( k = count ; k - - > 0 ; ) {
if ( mask & & ! ( mask & ( 1 < < k ) ) )
continue ;
if ( i - > max = = list [ k ] & & ! i - > openmax )
goto _l2 ;
if ( i - > max > list [ k ] ) {
i - > max = list [ k ] ;
i - > openmax = 0 ;
changed = 1 ;
goto _l2 ;
}
}
i - > empty = 1 ;
return - EINVAL ;
_l2 :
if ( snd_interval_checkempty ( i ) ) {
i - > empty = 1 ;
return - EINVAL ;
}
return changed ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_interval_list ) ;
2005-11-17 15:59:38 +03:00
static int snd_interval_step ( struct snd_interval * i , unsigned int min , unsigned int step )
2005-04-17 02:20:36 +04:00
{
unsigned int n ;
int changed = 0 ;
n = ( i - > min - min ) % step ;
if ( n ! = 0 | | i - > openmin ) {
i - > min + = step - n ;
changed = 1 ;
}
n = ( i - > max - min ) % step ;
if ( n ! = 0 | | i - > openmax ) {
i - > max - = n ;
changed = 1 ;
}
if ( snd_interval_checkempty ( i ) ) {
i - > empty = 1 ;
return - EINVAL ;
}
return changed ;
}
/* Info constraints helpers */
/**
* snd_pcm_hw_rule_add - add the hw - constraint rule
* @ runtime : the pcm runtime instance
* @ cond : condition bits
* @ var : the variable to evaluate
* @ func : the evaluation function
* @ private : the private data pointer passed to function
* @ dep : the dependent variables
*
* Returns zero if successful , or a negative error code on failure .
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_hw_rule_add ( struct snd_pcm_runtime * runtime , unsigned int cond ,
2005-04-17 02:20:36 +04:00
int var ,
snd_pcm_hw_rule_func_t func , void * private ,
int dep , . . . )
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_hw_constraints * constrs = & runtime - > hw_constraints ;
struct snd_pcm_hw_rule * c ;
2005-04-17 02:20:36 +04:00
unsigned int k ;
va_list args ;
va_start ( args , dep ) ;
if ( constrs - > rules_num > = constrs - > rules_all ) {
2005-11-17 15:59:38 +03:00
struct snd_pcm_hw_rule * new ;
2005-04-17 02:20:36 +04:00
unsigned int new_rules = constrs - > rules_all + 16 ;
new = kcalloc ( new_rules , sizeof ( * c ) , GFP_KERNEL ) ;
if ( ! new )
return - ENOMEM ;
if ( constrs - > rules ) {
memcpy ( new , constrs - > rules ,
constrs - > rules_num * sizeof ( * c ) ) ;
kfree ( constrs - > rules ) ;
}
constrs - > rules = new ;
constrs - > rules_all = new_rules ;
}
c = & constrs - > rules [ constrs - > rules_num ] ;
c - > cond = cond ;
c - > func = func ;
c - > var = var ;
c - > private = private ;
k = 0 ;
while ( 1 ) {
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( k > = ARRAY_SIZE ( c - > deps ) ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
c - > deps [ k + + ] = dep ;
if ( dep < 0 )
break ;
dep = va_arg ( args , int ) ;
}
constrs - > rules_num + + ;
va_end ( args ) ;
return 0 ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_hw_rule_add ) ;
2005-04-17 02:20:36 +04:00
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_hw_constraint_mask - apply the given bitmap mask constraint
2005-09-07 15:38:19 +04:00
* @ runtime : PCM runtime instance
* @ var : hw_params variable to apply the mask
* @ mask : the bitmap mask
*
2008-10-16 01:38:40 +04:00
* Apply the constraint of the given bitmap mask to a 32 - bit mask parameter .
2005-04-17 02:20:36 +04:00
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_hw_constraint_mask ( struct snd_pcm_runtime * runtime , snd_pcm_hw_param_t var ,
2005-04-17 02:20:36 +04:00
u_int32_t mask )
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_hw_constraints * constrs = & runtime - > hw_constraints ;
struct snd_mask * maskp = constrs_mask ( constrs , var ) ;
2005-04-17 02:20:36 +04:00
* maskp - > bits & = mask ;
memset ( maskp - > bits + 1 , 0 , ( SNDRV_MASK_MAX - 32 ) / 8 ) ; /* clear rest */
if ( * maskp - > bits = = 0 )
return - EINVAL ;
return 0 ;
}
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_hw_constraint_mask64 - apply the given bitmap mask constraint
2005-09-07 15:38:19 +04:00
* @ runtime : PCM runtime instance
* @ var : hw_params variable to apply the mask
* @ mask : the 64 bit bitmap mask
*
2008-10-16 01:38:40 +04:00
* Apply the constraint of the given bitmap mask to a 64 - bit mask parameter .
2005-04-17 02:20:36 +04:00
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_hw_constraint_mask64 ( struct snd_pcm_runtime * runtime , snd_pcm_hw_param_t var ,
2005-04-17 02:20:36 +04:00
u_int64_t mask )
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_hw_constraints * constrs = & runtime - > hw_constraints ;
struct snd_mask * maskp = constrs_mask ( constrs , var ) ;
2005-04-17 02:20:36 +04:00
maskp - > bits [ 0 ] & = ( u_int32_t ) mask ;
maskp - > bits [ 1 ] & = ( u_int32_t ) ( mask > > 32 ) ;
memset ( maskp - > bits + 2 , 0 , ( SNDRV_MASK_MAX - 64 ) / 8 ) ; /* clear rest */
if ( ! maskp - > bits [ 0 ] & & ! maskp - > bits [ 1 ] )
return - EINVAL ;
return 0 ;
}
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_hw_constraint_integer - apply an integer constraint to an interval
2005-09-07 15:38:19 +04:00
* @ runtime : PCM runtime instance
* @ var : hw_params variable to apply the integer constraint
*
* Apply the constraint of integer to an interval parameter .
2005-04-17 02:20:36 +04:00
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_hw_constraint_integer ( struct snd_pcm_runtime * runtime , snd_pcm_hw_param_t var )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_hw_constraints * constrs = & runtime - > hw_constraints ;
2005-04-17 02:20:36 +04:00
return snd_interval_setinteger ( constrs_interval ( constrs , var ) ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_hw_constraint_integer ) ;
2005-04-17 02:20:36 +04:00
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_hw_constraint_minmax - apply a min / max range constraint to an interval
2005-09-07 15:38:19 +04:00
* @ runtime : PCM runtime instance
* @ var : hw_params variable to apply the range
* @ min : the minimal value
* @ max : the maximal value
*
* Apply the min / max range constraint to an interval parameter .
2005-04-17 02:20:36 +04:00
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_hw_constraint_minmax ( struct snd_pcm_runtime * runtime , snd_pcm_hw_param_t var ,
2005-04-17 02:20:36 +04:00
unsigned int min , unsigned int max )
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_hw_constraints * constrs = & runtime - > hw_constraints ;
struct snd_interval t ;
2005-04-17 02:20:36 +04:00
t . min = min ;
t . max = max ;
t . openmin = t . openmax = 0 ;
t . integer = 0 ;
return snd_interval_refine ( constrs_interval ( constrs , var ) , & t ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_hw_constraint_minmax ) ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_rule_list ( 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_hw_constraint_list * list = rule - > private ;
2005-04-17 02:20:36 +04:00
return snd_interval_list ( hw_param_interval ( params , rule - > var ) , list - > count , list - > list , list - > mask ) ;
}
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_hw_constraint_list - apply a list of constraints to a parameter
2005-09-07 15:38:19 +04:00
* @ runtime : PCM runtime instance
* @ cond : condition bits
* @ var : hw_params variable to apply the list constraint
* @ l : list
*
* Apply the list of constraints to an interval parameter .
2005-04-17 02:20:36 +04:00
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_hw_constraint_list ( struct snd_pcm_runtime * runtime ,
2005-04-17 02:20:36 +04:00
unsigned int cond ,
snd_pcm_hw_param_t var ,
2005-11-17 15:59:38 +03:00
struct snd_pcm_hw_constraint_list * l )
2005-04-17 02:20:36 +04:00
{
return snd_pcm_hw_rule_add ( runtime , cond , var ,
snd_pcm_hw_rule_list , l ,
var , - 1 ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_hw_constraint_list ) ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_rule_ratnums ( 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_hw_constraint_ratnums * r = rule - > private ;
2005-04-17 02:20:36 +04:00
unsigned int num = 0 , den = 0 ;
int err ;
err = snd_interval_ratnum ( hw_param_interval ( params , rule - > var ) ,
r - > nrats , r - > rats , & num , & den ) ;
if ( err > = 0 & & den & & rule - > var = = SNDRV_PCM_HW_PARAM_RATE ) {
params - > rate_num = num ;
params - > rate_den = den ;
}
return err ;
}
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_hw_constraint_ratnums - apply ratnums constraint to a parameter
2005-09-07 15:38:19 +04:00
* @ runtime : PCM runtime instance
* @ cond : condition bits
* @ var : hw_params variable to apply the ratnums constraint
2005-11-17 15:59:38 +03:00
* @ r : struct snd_ratnums constriants
2005-04-17 02:20:36 +04:00
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_hw_constraint_ratnums ( struct snd_pcm_runtime * runtime ,
2005-04-17 02:20:36 +04:00
unsigned int cond ,
snd_pcm_hw_param_t var ,
2005-11-17 15:59:38 +03:00
struct snd_pcm_hw_constraint_ratnums * r )
2005-04-17 02:20:36 +04:00
{
return snd_pcm_hw_rule_add ( runtime , cond , var ,
snd_pcm_hw_rule_ratnums , r ,
var , - 1 ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_hw_constraint_ratnums ) ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_rule_ratdens ( 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_hw_constraint_ratdens * r = rule - > private ;
2005-04-17 02:20:36 +04:00
unsigned int num = 0 , den = 0 ;
int err = snd_interval_ratden ( hw_param_interval ( params , rule - > var ) ,
r - > nrats , r - > rats , & num , & den ) ;
if ( err > = 0 & & den & & rule - > var = = SNDRV_PCM_HW_PARAM_RATE ) {
params - > rate_num = num ;
params - > rate_den = den ;
}
return err ;
}
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_hw_constraint_ratdens - apply ratdens constraint to a parameter
2005-09-07 15:38:19 +04:00
* @ runtime : PCM runtime instance
* @ cond : condition bits
* @ var : hw_params variable to apply the ratdens constraint
2005-11-17 15:59:38 +03:00
* @ r : struct snd_ratdens constriants
2005-04-17 02:20:36 +04:00
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_hw_constraint_ratdens ( struct snd_pcm_runtime * runtime ,
2005-04-17 02:20:36 +04:00
unsigned int cond ,
snd_pcm_hw_param_t var ,
2005-11-17 15:59:38 +03:00
struct snd_pcm_hw_constraint_ratdens * r )
2005-04-17 02:20:36 +04:00
{
return snd_pcm_hw_rule_add ( runtime , cond , var ,
snd_pcm_hw_rule_ratdens , r ,
var , - 1 ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_hw_constraint_ratdens ) ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_rule_msbits ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
2005-04-17 02:20:36 +04:00
{
unsigned int l = ( unsigned long ) rule - > private ;
int width = l & 0xffff ;
unsigned int msbits = l > > 16 ;
2005-11-17 15:59:38 +03:00
struct snd_interval * i = hw_param_interval ( params , SNDRV_PCM_HW_PARAM_SAMPLE_BITS ) ;
2005-04-17 02:20:36 +04:00
if ( snd_interval_single ( i ) & & snd_interval_value ( i ) = = width )
params - > msbits = msbits ;
return 0 ;
}
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_hw_constraint_msbits - add a hw constraint msbits rule
2005-09-07 15:38:19 +04:00
* @ runtime : PCM runtime instance
* @ cond : condition bits
* @ width : sample bits width
* @ msbits : msbits width
2005-04-17 02:20:36 +04:00
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_hw_constraint_msbits ( struct snd_pcm_runtime * runtime ,
2005-04-17 02:20:36 +04:00
unsigned int cond ,
unsigned int width ,
unsigned int msbits )
{
unsigned long l = ( msbits < < 16 ) | width ;
return snd_pcm_hw_rule_add ( runtime , cond , - 1 ,
snd_pcm_hw_rule_msbits ,
( void * ) l ,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS , - 1 ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_hw_constraint_msbits ) ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_rule_step ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
2005-04-17 02:20:36 +04:00
{
unsigned long step = ( unsigned long ) rule - > private ;
return snd_interval_step ( hw_param_interval ( params , rule - > var ) , 0 , step ) ;
}
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_hw_constraint_step - add a hw constraint step rule
2005-09-07 15:38:19 +04:00
* @ runtime : PCM runtime instance
* @ cond : condition bits
* @ var : hw_params variable to apply the step constraint
* @ step : step size
2005-04-17 02:20:36 +04:00
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_hw_constraint_step ( struct snd_pcm_runtime * runtime ,
2005-04-17 02:20:36 +04:00
unsigned int cond ,
snd_pcm_hw_param_t var ,
unsigned long step )
{
return snd_pcm_hw_rule_add ( runtime , cond , var ,
snd_pcm_hw_rule_step , ( void * ) step ,
var , - 1 ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_hw_constraint_step ) ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_hw_rule_pow2 ( struct snd_pcm_hw_params * params , struct snd_pcm_hw_rule * rule )
2005-04-17 02:20:36 +04:00
{
2007-12-14 14:53:21 +03:00
static unsigned int pow2_sizes [ ] = {
2005-04-17 02:20:36 +04:00
1 < < 0 , 1 < < 1 , 1 < < 2 , 1 < < 3 , 1 < < 4 , 1 < < 5 , 1 < < 6 , 1 < < 7 ,
1 < < 8 , 1 < < 9 , 1 < < 10 , 1 < < 11 , 1 < < 12 , 1 < < 13 , 1 < < 14 , 1 < < 15 ,
1 < < 16 , 1 < < 17 , 1 < < 18 , 1 < < 19 , 1 < < 20 , 1 < < 21 , 1 < < 22 , 1 < < 23 ,
1 < < 24 , 1 < < 25 , 1 < < 26 , 1 < < 27 , 1 < < 28 , 1 < < 29 , 1 < < 30
} ;
return snd_interval_list ( hw_param_interval ( params , rule - > var ) ,
ARRAY_SIZE ( pow2_sizes ) , pow2_sizes , 0 ) ;
}
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_hw_constraint_pow2 - add a hw constraint power - of - 2 rule
2005-09-07 15:38:19 +04:00
* @ runtime : PCM runtime instance
* @ cond : condition bits
* @ var : hw_params variable to apply the power - of - 2 constraint
2005-04-17 02:20:36 +04:00
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_hw_constraint_pow2 ( struct snd_pcm_runtime * runtime ,
2005-04-17 02:20:36 +04:00
unsigned int cond ,
snd_pcm_hw_param_t var )
{
return snd_pcm_hw_rule_add ( runtime , cond , var ,
snd_pcm_hw_rule_pow2 , NULL ,
var , - 1 ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_hw_constraint_pow2 ) ;
2005-11-17 15:59:38 +03:00
static void _snd_pcm_hw_param_any ( struct snd_pcm_hw_params * params ,
2005-05-18 20:02:04 +04:00
snd_pcm_hw_param_t var )
2005-04-17 02:20:36 +04:00
{
if ( hw_is_mask ( var ) ) {
snd_mask_any ( hw_param_mask ( params , var ) ) ;
params - > cmask | = 1 < < var ;
params - > rmask | = 1 < < var ;
return ;
}
if ( hw_is_interval ( var ) ) {
snd_interval_any ( hw_param_interval ( params , var ) ) ;
params - > cmask | = 1 < < var ;
params - > rmask | = 1 < < var ;
return ;
}
snd_BUG ( ) ;
}
2005-11-17 15:59:38 +03:00
void _snd_pcm_hw_params_any ( struct snd_pcm_hw_params * params )
2005-04-17 02:20:36 +04:00
{
unsigned int k ;
memset ( params , 0 , sizeof ( * params ) ) ;
for ( k = SNDRV_PCM_HW_PARAM_FIRST_MASK ; k < = SNDRV_PCM_HW_PARAM_LAST_MASK ; k + + )
_snd_pcm_hw_param_any ( params , k ) ;
for ( k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL ; k < = SNDRV_PCM_HW_PARAM_LAST_INTERVAL ; k + + )
_snd_pcm_hw_param_any ( params , k ) ;
params - > info = ~ 0U ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( _snd_pcm_hw_params_any ) ;
2005-04-17 02:20:36 +04:00
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_hw_param_value - return @ params field @ var value
2005-09-07 15:38:19 +04:00
* @ params : the hw_params instance
* @ var : parameter to retrieve
2008-10-16 01:38:40 +04:00
* @ dir : pointer to the direction ( - 1 , 0 , 1 ) or % NULL
2005-04-17 02:20:36 +04:00
*
2008-10-16 01:38:40 +04:00
* Return the value for field @ var if it ' s fixed in configuration space
* defined by @ params . Return - % EINVAL otherwise .
2005-04-17 02:20:36 +04:00
*/
2006-04-28 17:13:40 +04:00
int snd_pcm_hw_param_value ( const struct snd_pcm_hw_params * params ,
snd_pcm_hw_param_t var , int * dir )
2005-04-17 02:20:36 +04:00
{
if ( hw_is_mask ( var ) ) {
2005-11-17 15:59:38 +03:00
const struct snd_mask * mask = hw_param_mask_c ( params , var ) ;
2005-04-17 02:20:36 +04:00
if ( ! snd_mask_single ( mask ) )
return - EINVAL ;
if ( dir )
* dir = 0 ;
return snd_mask_value ( mask ) ;
}
if ( hw_is_interval ( var ) ) {
2005-11-17 15:59:38 +03:00
const struct snd_interval * i = hw_param_interval_c ( params , var ) ;
2005-04-17 02:20:36 +04:00
if ( ! snd_interval_single ( i ) )
return - EINVAL ;
if ( dir )
* dir = i - > openmin ;
return snd_interval_value ( i ) ;
}
return - EINVAL ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_hw_param_value ) ;
2005-04-17 02:20:36 +04:00
2005-11-17 15:59:38 +03:00
void _snd_pcm_hw_param_setempty ( struct snd_pcm_hw_params * params ,
2005-04-17 02:20:36 +04:00
snd_pcm_hw_param_t var )
{
if ( hw_is_mask ( var ) ) {
snd_mask_none ( hw_param_mask ( params , var ) ) ;
params - > cmask | = 1 < < var ;
params - > rmask | = 1 < < var ;
} else if ( hw_is_interval ( var ) ) {
snd_interval_none ( hw_param_interval ( params , var ) ) ;
params - > cmask | = 1 < < var ;
params - > rmask | = 1 < < var ;
} else {
snd_BUG ( ) ;
}
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( _snd_pcm_hw_param_setempty ) ;
2005-04-17 02:20:36 +04:00
2005-11-17 15:59:38 +03:00
static int _snd_pcm_hw_param_first ( struct snd_pcm_hw_params * params ,
2005-05-18 20:02:04 +04:00
snd_pcm_hw_param_t var )
2005-04-17 02:20:36 +04:00
{
int changed ;
if ( hw_is_mask ( var ) )
changed = snd_mask_refine_first ( hw_param_mask ( params , var ) ) ;
else if ( hw_is_interval ( var ) )
changed = snd_interval_refine_first ( hw_param_interval ( params , var ) ) ;
2006-04-28 17:13:40 +04:00
else
2005-04-17 02:20:36 +04:00
return - EINVAL ;
if ( changed ) {
params - > cmask | = 1 < < var ;
params - > rmask | = 1 < < var ;
}
return changed ;
}
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_hw_param_first - refine config space and return minimum value
2005-09-07 15:38:19 +04:00
* @ pcm : PCM instance
* @ params : the hw_params instance
* @ var : parameter to retrieve
2008-10-16 01:38:40 +04:00
* @ dir : pointer to the direction ( - 1 , 0 , 1 ) or % NULL
2005-04-17 02:20:36 +04:00
*
2008-10-16 01:38:40 +04:00
* Inside configuration space defined by @ params remove from @ var all
2005-04-17 02:20:36 +04:00
* values > minimum . Reduce configuration space accordingly .
* Return the minimum .
*/
2006-04-28 17:13:40 +04:00
int snd_pcm_hw_param_first ( struct snd_pcm_substream * pcm ,
struct snd_pcm_hw_params * params ,
snd_pcm_hw_param_t var , int * dir )
2005-04-17 02:20:36 +04:00
{
int changed = _snd_pcm_hw_param_first ( params , var ) ;
if ( changed < 0 )
return changed ;
if ( params - > rmask ) {
int err = snd_pcm_hw_refine ( pcm , params ) ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( err < 0 ) )
return err ;
2005-04-17 02:20:36 +04:00
}
return snd_pcm_hw_param_value ( params , var , dir ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_hw_param_first ) ;
2005-11-17 15:59:38 +03:00
static int _snd_pcm_hw_param_last ( struct snd_pcm_hw_params * params ,
2005-05-18 20:02:04 +04:00
snd_pcm_hw_param_t var )
2005-04-17 02:20:36 +04:00
{
int changed ;
if ( hw_is_mask ( var ) )
changed = snd_mask_refine_last ( hw_param_mask ( params , var ) ) ;
else if ( hw_is_interval ( var ) )
changed = snd_interval_refine_last ( hw_param_interval ( params , var ) ) ;
2006-04-28 17:13:40 +04:00
else
2005-04-17 02:20:36 +04:00
return - EINVAL ;
if ( changed ) {
params - > cmask | = 1 < < var ;
params - > rmask | = 1 < < var ;
}
return changed ;
}
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_hw_param_last - refine config space and return maximum value
2005-09-07 15:38:19 +04:00
* @ pcm : PCM instance
* @ params : the hw_params instance
* @ var : parameter to retrieve
2008-10-16 01:38:40 +04:00
* @ dir : pointer to the direction ( - 1 , 0 , 1 ) or % NULL
2005-04-17 02:20:36 +04:00
*
2008-10-16 01:38:40 +04:00
* Inside configuration space defined by @ params remove from @ var all
2005-04-17 02:20:36 +04:00
* values < maximum . Reduce configuration space accordingly .
* Return the maximum .
*/
2006-04-28 17:13:40 +04:00
int snd_pcm_hw_param_last ( struct snd_pcm_substream * pcm ,
struct snd_pcm_hw_params * params ,
snd_pcm_hw_param_t var , int * dir )
2005-04-17 02:20:36 +04:00
{
int changed = _snd_pcm_hw_param_last ( params , var ) ;
if ( changed < 0 )
return changed ;
if ( params - > rmask ) {
int err = snd_pcm_hw_refine ( pcm , params ) ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( err < 0 ) )
return err ;
2005-04-17 02:20:36 +04:00
}
return snd_pcm_hw_param_value ( params , var , dir ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_hw_param_last ) ;
2005-04-17 02:20:36 +04:00
/**
2008-10-16 01:38:40 +04:00
* snd_pcm_hw_param_choose - choose a configuration defined by @ params
2005-09-07 15:38:19 +04:00
* @ pcm : PCM instance
* @ params : the hw_params instance
2005-04-17 02:20:36 +04:00
*
2008-10-16 01:38:40 +04:00
* Choose one configuration from configuration space defined by @ params .
2005-04-17 02:20:36 +04:00
* The configuration chosen is that obtained fixing in this order :
* first access , first format , first subformat , min channels ,
* min rate , min period time , max buffer size , min tick time
*/
2006-04-28 17:13:40 +04:00
int snd_pcm_hw_params_choose ( struct snd_pcm_substream * pcm ,
struct snd_pcm_hw_params * params )
2005-04-17 02:20:36 +04:00
{
2006-04-28 17:13:40 +04:00
static int vars [ ] = {
SNDRV_PCM_HW_PARAM_ACCESS ,
SNDRV_PCM_HW_PARAM_FORMAT ,
SNDRV_PCM_HW_PARAM_SUBFORMAT ,
SNDRV_PCM_HW_PARAM_CHANNELS ,
SNDRV_PCM_HW_PARAM_RATE ,
SNDRV_PCM_HW_PARAM_PERIOD_TIME ,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE ,
SNDRV_PCM_HW_PARAM_TICK_TIME ,
- 1
} ;
int err , * v ;
2005-04-17 02:20:36 +04:00
2006-04-28 17:13:40 +04:00
for ( v = vars ; * v ! = - 1 ; v + + ) {
if ( * v ! = SNDRV_PCM_HW_PARAM_BUFFER_SIZE )
err = snd_pcm_hw_param_first ( pcm , params , * v , NULL ) ;
else
err = snd_pcm_hw_param_last ( pcm , params , * v , NULL ) ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( err < 0 ) )
return err ;
2006-04-28 17:13:40 +04:00
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_lib_ioctl_reset ( struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
void * arg )
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
snd_pcm_stream_lock_irqsave ( substream , flags ) ;
if ( snd_pcm_running ( substream ) & &
snd_pcm_update_hw_ptr ( substream ) > = 0 )
runtime - > status - > hw_ptr % = runtime - > buffer_size ;
else
runtime - > status - > hw_ptr = 0 ;
2009-04-10 14:28:58 +04:00
runtime - > hw_ptr_jiffies = jiffies ;
2005-04-17 02:20:36 +04:00
snd_pcm_stream_unlock_irqrestore ( substream , flags ) ;
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_lib_ioctl_channel_info ( struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
void * arg )
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_channel_info * info = arg ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
int width ;
if ( ! ( runtime - > info & SNDRV_PCM_INFO_MMAP ) ) {
info - > offset = - 1 ;
return 0 ;
}
width = snd_pcm_format_physical_width ( runtime - > format ) ;
if ( width < 0 )
return width ;
info - > offset = 0 ;
switch ( runtime - > access ) {
case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED :
case SNDRV_PCM_ACCESS_RW_INTERLEAVED :
info - > first = info - > channel * width ;
info - > step = runtime - > channels * width ;
break ;
case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED :
case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED :
{
size_t size = runtime - > dma_bytes / runtime - > channels ;
info - > first = info - > channel * size * 8 ;
info - > step = width ;
break ;
}
default :
snd_BUG ( ) ;
break ;
}
return 0 ;
}
/**
* snd_pcm_lib_ioctl - a generic PCM ioctl callback
* @ substream : the pcm substream instance
* @ cmd : ioctl command
* @ arg : ioctl argument
*
* Processes the generic ioctl commands for PCM .
* Can be passed as the ioctl callback for PCM ops .
*
* Returns zero if successful , or a negative error code on failure .
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_lib_ioctl ( struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
unsigned int cmd , void * arg )
{
switch ( cmd ) {
case SNDRV_PCM_IOCTL1_INFO :
return 0 ;
case SNDRV_PCM_IOCTL1_RESET :
return snd_pcm_lib_ioctl_reset ( substream , arg ) ;
case SNDRV_PCM_IOCTL1_CHANNEL_INFO :
return snd_pcm_lib_ioctl_channel_info ( substream , arg ) ;
}
return - ENXIO ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_lib_ioctl ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_pcm_period_elapsed - update the pcm status for the next period
* @ substream : the pcm substream instance
*
* This function is called from the interrupt handler when the
* PCM has processed the period size . It will update the current
2008-01-08 20:09:57 +03:00
* pointer , wake up sleepers , etc .
2005-04-17 02:20:36 +04:00
*
* Even if more than one periods have elapsed since the last call , you
* have to call this only once .
*/
2005-11-17 15:59:38 +03:00
void snd_pcm_period_elapsed ( 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
unsigned long flags ;
2008-08-08 19:09:09 +04:00
if ( PCM_RUNTIME_CHECK ( substream ) )
return ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
if ( runtime - > transfer_ack_begin )
runtime - > transfer_ack_begin ( substream ) ;
snd_pcm_stream_lock_irqsave ( substream , flags ) ;
if ( ! snd_pcm_running ( substream ) | |
snd_pcm_update_hw_ptr_interrupt ( substream ) < 0 )
goto _end ;
if ( substream - > timer_running )
snd_timer_interrupt ( substream - > timer , 1 ) ;
_end :
snd_pcm_stream_unlock_irqrestore ( substream , flags ) ;
if ( runtime - > transfer_ack_end )
runtime - > transfer_ack_end ( substream ) ;
kill_fasync ( & runtime - > fasync , SIGIO , POLL_IN ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_period_elapsed ) ;
2008-01-08 20:08:14 +03:00
/*
* Wait until avail_min data becomes available
* Returns a negative error code if any error occurs during operation .
* The available space is stored on availp . When err = 0 and avail = 0
* on the capture stream , it indicates the stream is in DRAINING state .
*/
static int wait_for_avail_min ( struct snd_pcm_substream * substream ,
snd_pcm_uframes_t * availp )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
int is_playback = substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ;
wait_queue_t wait ;
int err = 0 ;
snd_pcm_uframes_t avail = 0 ;
long tout ;
init_waitqueue_entry ( & wait , current ) ;
add_wait_queue ( & runtime - > sleep , & wait ) ;
for ( ; ; ) {
if ( signal_pending ( current ) ) {
err = - ERESTARTSYS ;
break ;
}
set_current_state ( TASK_INTERRUPTIBLE ) ;
snd_pcm_stream_unlock_irq ( substream ) ;
tout = schedule_timeout ( msecs_to_jiffies ( 10000 ) ) ;
snd_pcm_stream_lock_irq ( substream ) ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_SUSPENDED :
err = - ESTRPIPE ;
goto _endloop ;
case SNDRV_PCM_STATE_XRUN :
err = - EPIPE ;
goto _endloop ;
case SNDRV_PCM_STATE_DRAINING :
if ( is_playback )
err = - EPIPE ;
else
avail = 0 ; /* indicate draining */
goto _endloop ;
case SNDRV_PCM_STATE_OPEN :
case SNDRV_PCM_STATE_SETUP :
case SNDRV_PCM_STATE_DISCONNECTED :
err = - EBADFD ;
goto _endloop ;
}
if ( ! tout ) {
snd_printd ( " %s write error (DMA or IRQ trouble?) \n " ,
is_playback ? " playback " : " capture " ) ;
err = - EIO ;
break ;
}
if ( is_playback )
avail = snd_pcm_playback_avail ( runtime ) ;
else
avail = snd_pcm_capture_avail ( runtime ) ;
if ( avail > = runtime - > control - > avail_min )
break ;
}
_endloop :
remove_wait_queue ( & runtime - > sleep , & wait ) ;
* availp = avail ;
return err ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_lib_write_transfer ( struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
unsigned int hwoff ,
unsigned long data , unsigned int off ,
snd_pcm_uframes_t frames )
{
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 ;
char __user * buf = ( char __user * ) data + frames_to_bytes ( runtime , off ) ;
if ( substream - > ops - > copy ) {
if ( ( err = substream - > ops - > copy ( substream , - 1 , hwoff , buf , frames ) ) < 0 )
return err ;
} else {
char * hwbuf = runtime - > dma_area + frames_to_bytes ( runtime , hwoff ) ;
if ( copy_from_user ( hwbuf , buf , frames_to_bytes ( runtime , frames ) ) )
return - EFAULT ;
}
return 0 ;
}
2005-11-17 15:59:38 +03:00
typedef int ( * transfer_f ) ( struct snd_pcm_substream * substream , unsigned int hwoff ,
2005-04-17 02:20:36 +04:00
unsigned long data , unsigned int off ,
snd_pcm_uframes_t size ) ;
2005-11-17 15:59:38 +03:00
static snd_pcm_sframes_t snd_pcm_lib_write1 ( struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
unsigned long data ,
snd_pcm_uframes_t size ,
int nonblock ,
transfer_f transfer )
{
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_uframes_t xfer = 0 ;
snd_pcm_uframes_t offset = 0 ;
int err = 0 ;
if ( size = = 0 )
return 0 ;
snd_pcm_stream_lock_irq ( substream ) ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_PREPARED :
case SNDRV_PCM_STATE_RUNNING :
case SNDRV_PCM_STATE_PAUSED :
break ;
case SNDRV_PCM_STATE_XRUN :
err = - EPIPE ;
goto _end_unlock ;
case SNDRV_PCM_STATE_SUSPENDED :
err = - ESTRPIPE ;
goto _end_unlock ;
default :
err = - EBADFD ;
goto _end_unlock ;
}
while ( size > 0 ) {
snd_pcm_uframes_t frames , appl_ptr , appl_ofs ;
snd_pcm_uframes_t avail ;
snd_pcm_uframes_t cont ;
2008-01-08 20:09:57 +03:00
if ( runtime - > status - > state = = SNDRV_PCM_STATE_RUNNING )
2005-04-17 02:20:36 +04:00
snd_pcm_update_hw_ptr ( substream ) ;
avail = snd_pcm_playback_avail ( runtime ) ;
2008-01-08 20:08:14 +03:00
if ( ! avail ) {
2005-04-17 02:20:36 +04:00
if ( nonblock ) {
err = - EAGAIN ;
goto _end_unlock ;
}
2008-01-08 20:08:14 +03:00
err = wait_for_avail_min ( substream , & avail ) ;
if ( err < 0 )
2005-08-10 13:18:19 +04:00
goto _end_unlock ;
2005-04-17 02:20:36 +04:00
}
frames = size > avail ? avail : size ;
cont = runtime - > buffer_size - runtime - > control - > appl_ptr % runtime - > buffer_size ;
if ( frames > cont )
frames = cont ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! frames ) ) {
snd_pcm_stream_unlock_irq ( substream ) ;
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
appl_ptr = runtime - > control - > appl_ptr ;
appl_ofs = appl_ptr % runtime - > buffer_size ;
snd_pcm_stream_unlock_irq ( substream ) ;
if ( ( err = transfer ( substream , appl_ofs , data , offset , frames ) ) < 0 )
goto _end ;
snd_pcm_stream_lock_irq ( substream ) ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_XRUN :
err = - EPIPE ;
goto _end_unlock ;
case SNDRV_PCM_STATE_SUSPENDED :
err = - ESTRPIPE ;
goto _end_unlock ;
default :
break ;
}
appl_ptr + = frames ;
if ( appl_ptr > = runtime - > boundary )
appl_ptr - = runtime - > boundary ;
runtime - > control - > appl_ptr = appl_ptr ;
if ( substream - > ops - > ack )
substream - > ops - > ack ( substream ) ;
offset + = frames ;
size - = frames ;
xfer + = frames ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_PREPARED & &
snd_pcm_playback_hw_avail ( runtime ) > = ( snd_pcm_sframes_t ) runtime - > start_threshold ) {
err = snd_pcm_start ( substream ) ;
if ( err < 0 )
goto _end_unlock ;
}
}
_end_unlock :
snd_pcm_stream_unlock_irq ( substream ) ;
_end :
return xfer > 0 ? ( snd_pcm_sframes_t ) xfer : err ;
}
2008-08-08 19:09:09 +04:00
/* sanity-check for read/write methods */
static int pcm_sanity_check ( 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 ;
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 ( snd_BUG_ON ( ! substream - > ops - > copy & & ! runtime - > dma_area ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN )
return - EBADFD ;
2008-08-08 19:09:09 +04:00
return 0 ;
}
snd_pcm_sframes_t snd_pcm_lib_write ( struct snd_pcm_substream * substream , const void __user * buf , snd_pcm_uframes_t size )
{
struct snd_pcm_runtime * runtime ;
int nonblock ;
int err ;
2005-04-17 02:20:36 +04:00
2008-08-08 19:09:09 +04:00
err = pcm_sanity_check ( substream ) ;
if ( err < 0 )
return err ;
runtime = substream - > runtime ;
2006-04-28 17:13:41 +04:00
nonblock = ! ! ( substream - > f_flags & O_NONBLOCK ) ;
2005-04-17 02:20:36 +04:00
if ( runtime - > access ! = SNDRV_PCM_ACCESS_RW_INTERLEAVED & &
runtime - > channels > 1 )
return - EINVAL ;
return snd_pcm_lib_write1 ( substream , ( unsigned long ) buf , size , nonblock ,
snd_pcm_lib_write_transfer ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_lib_write ) ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_lib_writev_transfer ( struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
unsigned int hwoff ,
unsigned long data , unsigned int off ,
snd_pcm_uframes_t frames )
{
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 ;
void __user * * bufs = ( void __user * * ) data ;
int channels = runtime - > channels ;
int c ;
if ( substream - > ops - > copy ) {
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! substream - > ops - > silence ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
for ( c = 0 ; c < channels ; + + c , + + bufs ) {
if ( * bufs = = NULL ) {
if ( ( err = substream - > ops - > silence ( substream , c , hwoff , frames ) ) < 0 )
return err ;
} else {
char __user * buf = * bufs + samples_to_bytes ( runtime , off ) ;
if ( ( err = substream - > ops - > copy ( substream , c , hwoff , buf , frames ) ) < 0 )
return err ;
}
}
} else {
/* default transfer behaviour */
size_t dma_csize = runtime - > dma_bytes / channels ;
for ( c = 0 ; c < channels ; + + c , + + bufs ) {
char * hwbuf = runtime - > dma_area + ( c * dma_csize ) + samples_to_bytes ( runtime , hwoff ) ;
if ( * bufs = = NULL ) {
snd_pcm_format_set_silence ( runtime - > format , hwbuf , frames ) ;
} else {
char __user * buf = * bufs + samples_to_bytes ( runtime , off ) ;
if ( copy_from_user ( hwbuf , buf , samples_to_bytes ( runtime , frames ) ) )
return - EFAULT ;
}
}
}
return 0 ;
}
2005-11-17 15:59:38 +03:00
snd_pcm_sframes_t snd_pcm_lib_writev ( struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
void __user * * bufs ,
snd_pcm_uframes_t frames )
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime ;
2005-04-17 02:20:36 +04:00
int nonblock ;
2008-08-08 19:09:09 +04:00
int err ;
2005-04-17 02:20:36 +04:00
2008-08-08 19:09:09 +04:00
err = pcm_sanity_check ( substream ) ;
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
2006-04-28 17:13:41 +04:00
nonblock = ! ! ( substream - > f_flags & O_NONBLOCK ) ;
2005-04-17 02:20:36 +04:00
if ( runtime - > access ! = SNDRV_PCM_ACCESS_RW_NONINTERLEAVED )
return - EINVAL ;
return snd_pcm_lib_write1 ( substream , ( unsigned long ) bufs , frames ,
nonblock , snd_pcm_lib_writev_transfer ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_lib_writev ) ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_lib_read_transfer ( struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
unsigned int hwoff ,
unsigned long data , unsigned int off ,
snd_pcm_uframes_t frames )
{
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 ;
char __user * buf = ( char __user * ) data + frames_to_bytes ( runtime , off ) ;
if ( substream - > ops - > copy ) {
if ( ( err = substream - > ops - > copy ( substream , - 1 , hwoff , buf , frames ) ) < 0 )
return err ;
} else {
char * hwbuf = runtime - > dma_area + frames_to_bytes ( runtime , hwoff ) ;
if ( copy_to_user ( buf , hwbuf , frames_to_bytes ( runtime , frames ) ) )
return - EFAULT ;
}
return 0 ;
}
2005-11-17 15:59:38 +03:00
static snd_pcm_sframes_t snd_pcm_lib_read1 ( struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
unsigned long data ,
snd_pcm_uframes_t size ,
int nonblock ,
transfer_f transfer )
{
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_uframes_t xfer = 0 ;
snd_pcm_uframes_t offset = 0 ;
int err = 0 ;
if ( size = = 0 )
return 0 ;
snd_pcm_stream_lock_irq ( substream ) ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_PREPARED :
if ( size > = runtime - > start_threshold ) {
err = snd_pcm_start ( substream ) ;
if ( err < 0 )
goto _end_unlock ;
}
break ;
case SNDRV_PCM_STATE_DRAINING :
case SNDRV_PCM_STATE_RUNNING :
case SNDRV_PCM_STATE_PAUSED :
break ;
case SNDRV_PCM_STATE_XRUN :
err = - EPIPE ;
goto _end_unlock ;
case SNDRV_PCM_STATE_SUSPENDED :
err = - ESTRPIPE ;
goto _end_unlock ;
default :
err = - EBADFD ;
goto _end_unlock ;
}
while ( size > 0 ) {
snd_pcm_uframes_t frames , appl_ptr , appl_ofs ;
snd_pcm_uframes_t avail ;
snd_pcm_uframes_t cont ;
2008-01-08 20:09:57 +03:00
if ( runtime - > status - > state = = SNDRV_PCM_STATE_RUNNING )
2005-04-17 02:20:36 +04:00
snd_pcm_update_hw_ptr ( substream ) ;
avail = snd_pcm_capture_avail ( runtime ) ;
2008-01-08 20:08:14 +03:00
if ( ! avail ) {
if ( runtime - > status - > state = =
SNDRV_PCM_STATE_DRAINING ) {
snd_pcm_stop ( substream , SNDRV_PCM_STATE_SETUP ) ;
2005-04-17 02:20:36 +04:00
goto _end_unlock ;
}
if ( nonblock ) {
err = - EAGAIN ;
goto _end_unlock ;
}
2008-01-08 20:08:14 +03:00
err = wait_for_avail_min ( substream , & avail ) ;
if ( err < 0 )
2005-08-10 13:18:19 +04:00
goto _end_unlock ;
2008-01-08 20:08:14 +03:00
if ( ! avail )
continue ; /* draining */
2005-04-17 02:20:36 +04:00
}
frames = size > avail ? avail : size ;
cont = runtime - > buffer_size - runtime - > control - > appl_ptr % runtime - > buffer_size ;
if ( frames > cont )
frames = cont ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! frames ) ) {
snd_pcm_stream_unlock_irq ( substream ) ;
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
appl_ptr = runtime - > control - > appl_ptr ;
appl_ofs = appl_ptr % runtime - > buffer_size ;
snd_pcm_stream_unlock_irq ( substream ) ;
if ( ( err = transfer ( substream , appl_ofs , data , offset , frames ) ) < 0 )
goto _end ;
snd_pcm_stream_lock_irq ( substream ) ;
switch ( runtime - > status - > state ) {
case SNDRV_PCM_STATE_XRUN :
err = - EPIPE ;
goto _end_unlock ;
case SNDRV_PCM_STATE_SUSPENDED :
err = - ESTRPIPE ;
goto _end_unlock ;
default :
break ;
}
appl_ptr + = frames ;
if ( appl_ptr > = runtime - > boundary )
appl_ptr - = runtime - > boundary ;
runtime - > control - > appl_ptr = appl_ptr ;
if ( substream - > ops - > ack )
substream - > ops - > ack ( substream ) ;
offset + = frames ;
size - = frames ;
xfer + = frames ;
}
_end_unlock :
snd_pcm_stream_unlock_irq ( substream ) ;
_end :
return xfer > 0 ? ( snd_pcm_sframes_t ) xfer : err ;
}
2005-11-17 15:59:38 +03:00
snd_pcm_sframes_t snd_pcm_lib_read ( struct snd_pcm_substream * substream , void __user * buf , snd_pcm_uframes_t size )
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 nonblock ;
2008-08-08 19:09:09 +04:00
int err ;
2005-04-17 02:20:36 +04:00
2008-08-08 19:09:09 +04:00
err = pcm_sanity_check ( substream ) ;
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
2006-04-28 17:13:41 +04:00
nonblock = ! ! ( substream - > f_flags & O_NONBLOCK ) ;
2005-04-17 02:20:36 +04:00
if ( runtime - > access ! = SNDRV_PCM_ACCESS_RW_INTERLEAVED )
return - EINVAL ;
return snd_pcm_lib_read1 ( substream , ( unsigned long ) buf , size , nonblock , snd_pcm_lib_read_transfer ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_lib_read ) ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_lib_readv_transfer ( struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
unsigned int hwoff ,
unsigned long data , unsigned int off ,
snd_pcm_uframes_t frames )
{
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 ;
void __user * * bufs = ( void __user * * ) data ;
int channels = runtime - > channels ;
int c ;
if ( substream - > ops - > copy ) {
for ( c = 0 ; c < channels ; + + c , + + bufs ) {
char __user * buf ;
if ( * bufs = = NULL )
continue ;
buf = * bufs + samples_to_bytes ( runtime , off ) ;
if ( ( err = substream - > ops - > copy ( substream , c , hwoff , buf , frames ) ) < 0 )
return err ;
}
} else {
snd_pcm_uframes_t dma_csize = runtime - > dma_bytes / channels ;
for ( c = 0 ; c < channels ; + + c , + + bufs ) {
char * hwbuf ;
char __user * buf ;
if ( * bufs = = NULL )
continue ;
hwbuf = runtime - > dma_area + ( c * dma_csize ) + samples_to_bytes ( runtime , hwoff ) ;
buf = * bufs + samples_to_bytes ( runtime , off ) ;
if ( copy_to_user ( buf , hwbuf , samples_to_bytes ( runtime , frames ) ) )
return - EFAULT ;
}
}
return 0 ;
}
2005-11-17 15:59:38 +03:00
snd_pcm_sframes_t snd_pcm_lib_readv ( struct snd_pcm_substream * substream ,
2005-04-17 02:20:36 +04:00
void __user * * bufs ,
snd_pcm_uframes_t frames )
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime ;
2005-04-17 02:20:36 +04:00
int nonblock ;
2008-08-08 19:09:09 +04:00
int err ;
2005-04-17 02:20:36 +04:00
2008-08-08 19:09:09 +04:00
err = pcm_sanity_check ( substream ) ;
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
runtime = substream - > runtime ;
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN )
return - EBADFD ;
2006-04-28 17:13:41 +04:00
nonblock = ! ! ( substream - > f_flags & O_NONBLOCK ) ;
2005-04-17 02:20:36 +04:00
if ( runtime - > access ! = SNDRV_PCM_ACCESS_RW_NONINTERLEAVED )
return - EINVAL ;
return snd_pcm_lib_read1 ( substream , ( unsigned long ) bufs , frames , nonblock , snd_pcm_lib_readv_transfer ) ;
}
EXPORT_SYMBOL ( snd_pcm_lib_readv ) ;