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/init.h>
# include <linux/slab.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# include <linux/time.h>
2006-01-16 18:29:08 +03:00
# include <linux/mutex.h>
2012-01-22 20:23:42 +04:00
# include <linux/device.h>
2005-04-17 02:20:36 +04:00
# include <sound/core.h>
# include <sound/minors.h>
# include <sound/pcm.h>
# include <sound/control.h>
# include <sound/info.h>
2007-10-15 11:50:19 +04:00
MODULE_AUTHOR ( " Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org> " ) ;
2005-04-17 02:20:36 +04:00
MODULE_DESCRIPTION ( " Midlevel PCM code for ALSA. " ) ;
MODULE_LICENSE ( " GPL " ) ;
2005-11-20 16:06:59 +03:00
static LIST_HEAD ( snd_pcm_devices ) ;
2005-04-17 02:20:36 +04:00
static LIST_HEAD ( snd_pcm_notify_list ) ;
2006-01-16 18:29:08 +03:00
static DEFINE_MUTEX ( register_mutex ) ;
2005-04-17 02:20:36 +04:00
2005-11-17 15:59:38 +03:00
static int snd_pcm_free ( struct snd_pcm * pcm ) ;
static int snd_pcm_dev_free ( struct snd_device * device ) ;
static int snd_pcm_dev_register ( struct snd_device * device ) ;
static int snd_pcm_dev_disconnect ( struct snd_device * device ) ;
2005-04-17 02:20:36 +04:00
2008-07-30 15:46:40 +04:00
static struct snd_pcm * snd_pcm_get ( struct snd_card * card , int device )
2005-11-20 16:06:59 +03:00
{
struct snd_pcm * pcm ;
2006-10-05 18:02:22 +04:00
list_for_each_entry ( pcm , & snd_pcm_devices , list ) {
2005-11-20 16:06:59 +03:00
if ( pcm - > card = = card & & pcm - > device = = device )
return pcm ;
}
return NULL ;
}
2008-07-30 15:46:40 +04:00
static int snd_pcm_next ( struct snd_card * card , int device )
{
struct snd_pcm * pcm ;
list_for_each_entry ( pcm , & snd_pcm_devices , list ) {
if ( pcm - > card = = card & & pcm - > device > device )
return pcm - > device ;
else if ( pcm - > card - > number > card - > number )
return - 1 ;
}
return - 1 ;
}
static int snd_pcm_add ( struct snd_pcm * newpcm )
{
struct snd_pcm * pcm ;
list_for_each_entry ( pcm , & snd_pcm_devices , list ) {
if ( pcm - > card = = newpcm - > card & & pcm - > device = = newpcm - > device )
return - EBUSY ;
if ( pcm - > card - > number > newpcm - > card - > number | |
( pcm - > card = = newpcm - > card & &
pcm - > device > newpcm - > device ) ) {
list_add ( & newpcm - > list , pcm - > list . prev ) ;
return 0 ;
}
}
list_add_tail ( & newpcm - > list , & snd_pcm_devices ) ;
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_control_ioctl ( struct snd_card * card ,
struct snd_ctl_file * control ,
2005-04-17 02:20:36 +04:00
unsigned int cmd , unsigned long arg )
{
switch ( cmd ) {
case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE :
{
int device ;
if ( get_user ( device , ( int __user * ) arg ) )
return - EFAULT ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2008-07-30 15:46:40 +04:00
device = snd_pcm_next ( card , device ) ;
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( put_user ( device , ( int __user * ) arg ) )
return - EFAULT ;
return 0 ;
}
case SNDRV_CTL_IOCTL_PCM_INFO :
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_info __user * info ;
2005-04-17 02:20:36 +04:00
unsigned int device , subdevice ;
2005-11-17 15:59:38 +03:00
int stream ;
struct snd_pcm * pcm ;
struct snd_pcm_str * pstr ;
struct snd_pcm_substream * substream ;
2005-11-20 16:06:59 +03:00
int err ;
2005-11-17 15:59:38 +03:00
info = ( struct snd_pcm_info __user * ) arg ;
2005-04-17 02:20:36 +04:00
if ( get_user ( device , & info - > device ) )
return - EFAULT ;
if ( get_user ( stream , & info - > stream ) )
return - EFAULT ;
if ( stream < 0 | | stream > 1 )
return - EINVAL ;
if ( get_user ( subdevice , & info - > subdevice ) )
return - EFAULT ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2008-07-30 15:46:40 +04:00
pcm = snd_pcm_get ( card , device ) ;
2005-11-20 16:06:59 +03:00
if ( pcm = = NULL ) {
err = - ENXIO ;
goto _error ;
}
pstr = & pcm - > streams [ stream ] ;
if ( pstr - > substream_count = = 0 ) {
err = - ENOENT ;
goto _error ;
}
if ( subdevice > = pstr - > substream_count ) {
err = - ENXIO ;
goto _error ;
}
for ( substream = pstr - > substream ; substream ;
substream = substream - > next )
2005-04-17 02:20:36 +04:00
if ( substream - > number = = ( int ) subdevice )
break ;
2005-11-20 16:06:59 +03:00
if ( substream = = NULL ) {
err = - ENXIO ;
goto _error ;
}
err = snd_pcm_info_user ( substream , info ) ;
_error :
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-11-20 16:06:59 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}
case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE :
{
int val ;
if ( get_user ( val , ( int __user * ) arg ) )
return - EFAULT ;
control - > prefer_pcm_subdevice = val ;
return 0 ;
}
}
return - ENOIOCTLCMD ;
}
2006-01-13 11:12:11 +03:00
2005-04-17 02:20:36 +04:00
# define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v
static char * snd_pcm_format_names [ ] = {
FORMAT ( S8 ) ,
FORMAT ( U8 ) ,
FORMAT ( S16_LE ) ,
FORMAT ( S16_BE ) ,
FORMAT ( U16_LE ) ,
FORMAT ( U16_BE ) ,
FORMAT ( S24_LE ) ,
FORMAT ( S24_BE ) ,
FORMAT ( U24_LE ) ,
FORMAT ( U24_BE ) ,
FORMAT ( S32_LE ) ,
FORMAT ( S32_BE ) ,
FORMAT ( U32_LE ) ,
FORMAT ( U32_BE ) ,
FORMAT ( FLOAT_LE ) ,
FORMAT ( FLOAT_BE ) ,
FORMAT ( FLOAT64_LE ) ,
FORMAT ( FLOAT64_BE ) ,
FORMAT ( IEC958_SUBFRAME_LE ) ,
FORMAT ( IEC958_SUBFRAME_BE ) ,
FORMAT ( MU_LAW ) ,
FORMAT ( A_LAW ) ,
FORMAT ( IMA_ADPCM ) ,
FORMAT ( MPEG ) ,
FORMAT ( GSM ) ,
FORMAT ( SPECIAL ) ,
FORMAT ( S24_3LE ) ,
FORMAT ( S24_3BE ) ,
FORMAT ( U24_3LE ) ,
FORMAT ( U24_3BE ) ,
FORMAT ( S20_3LE ) ,
FORMAT ( S20_3BE ) ,
FORMAT ( U20_3LE ) ,
FORMAT ( U20_3BE ) ,
FORMAT ( S18_3LE ) ,
FORMAT ( S18_3BE ) ,
FORMAT ( U18_3LE ) ,
FORMAT ( U18_3BE ) ,
2010-08-28 00:02:15 +04:00
FORMAT ( G723_24 ) ,
FORMAT ( G723_24_1B ) ,
FORMAT ( G723_40 ) ,
FORMAT ( G723_40_1B ) ,
2005-04-17 02:20:36 +04:00
} ;
2009-09-08 16:26:51 +04:00
const char * snd_pcm_format_name ( snd_pcm_format_t format )
2005-12-01 12:42:42 +03:00
{
2011-02-14 13:00:47 +03:00
if ( ( __force unsigned int ) format > = ARRAY_SIZE ( snd_pcm_format_names ) )
2010-08-28 00:02:15 +04:00
return " Unknown " ;
2011-02-14 13:00:47 +03:00
return snd_pcm_format_names [ ( __force unsigned int ) format ] ;
2005-12-01 12:42:42 +03:00
}
2009-09-08 16:26:51 +04:00
EXPORT_SYMBOL_GPL ( snd_pcm_format_name ) ;
# ifdef CONFIG_SND_VERBOSE_PROCFS
# define STATE(v) [SNDRV_PCM_STATE_##v] = #v
# define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v
# define READY(v) [SNDRV_PCM_READY_##v] = #v
# define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v
# define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v
# define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v
# define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v
# define START(v) [SNDRV_PCM_START_##v] = #v
# define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v
2005-12-01 12:42:42 +03:00
static char * snd_pcm_stream_names [ ] = {
STREAM ( PLAYBACK ) ,
STREAM ( CAPTURE ) ,
} ;
static char * snd_pcm_state_names [ ] = {
STATE ( OPEN ) ,
STATE ( SETUP ) ,
STATE ( PREPARED ) ,
STATE ( RUNNING ) ,
STATE ( XRUN ) ,
STATE ( DRAINING ) ,
STATE ( PAUSED ) ,
STATE ( SUSPENDED ) ,
} ;
static char * snd_pcm_access_names [ ] = {
ACCESS ( MMAP_INTERLEAVED ) ,
ACCESS ( MMAP_NONINTERLEAVED ) ,
ACCESS ( MMAP_COMPLEX ) ,
ACCESS ( RW_INTERLEAVED ) ,
ACCESS ( RW_NONINTERLEAVED ) ,
} ;
2005-04-17 02:20:36 +04:00
static char * snd_pcm_subformat_names [ ] = {
SUBFORMAT ( STD ) ,
} ;
static char * snd_pcm_tstamp_mode_names [ ] = {
TSTAMP ( NONE ) ,
2008-01-11 10:45:08 +03:00
TSTAMP ( ENABLE ) ,
2005-04-17 02:20:36 +04:00
} ;
2005-11-17 15:59:38 +03:00
static const char * snd_pcm_stream_name ( int stream )
2005-04-17 02:20:36 +04:00
{
return snd_pcm_stream_names [ stream ] ;
}
static const char * snd_pcm_access_name ( snd_pcm_access_t access )
{
2011-02-14 13:00:47 +03:00
return snd_pcm_access_names [ ( __force int ) access ] ;
2005-04-17 02:20:36 +04:00
}
static const char * snd_pcm_subformat_name ( snd_pcm_subformat_t subformat )
{
2011-02-14 13:00:47 +03:00
return snd_pcm_subformat_names [ ( __force int ) subformat ] ;
2005-04-17 02:20:36 +04:00
}
2005-11-17 15:59:38 +03:00
static const char * snd_pcm_tstamp_mode_name ( int mode )
2005-04-17 02:20:36 +04:00
{
return snd_pcm_tstamp_mode_names [ mode ] ;
}
static const char * snd_pcm_state_name ( snd_pcm_state_t state )
{
2011-02-14 13:00:47 +03:00
return snd_pcm_state_names [ ( __force int ) state ] ;
2005-04-17 02:20:36 +04:00
}
# if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
# include <linux/soundcard.h>
2006-01-16 18:29:08 +03:00
2005-04-17 02:20:36 +04:00
static const char * snd_pcm_oss_format_name ( int format )
{
switch ( format ) {
case AFMT_MU_LAW :
return " MU_LAW " ;
case AFMT_A_LAW :
return " A_LAW " ;
case AFMT_IMA_ADPCM :
return " IMA_ADPCM " ;
case AFMT_U8 :
return " U8 " ;
case AFMT_S16_LE :
return " S16_LE " ;
case AFMT_S16_BE :
return " S16_BE " ;
case AFMT_S8 :
return " S8 " ;
case AFMT_U16_LE :
return " U16_LE " ;
case AFMT_U16_BE :
return " U16_BE " ;
case AFMT_MPEG :
return " MPEG " ;
default :
return " unknown " ;
}
}
# endif
2005-11-17 15:59:38 +03:00
static void snd_pcm_proc_info_read ( struct snd_pcm_substream * substream ,
struct snd_info_buffer * buffer )
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 ;
2005-10-10 13:46:31 +04:00
if ( ! substream )
return ;
2005-04-17 02:20:36 +04:00
info = kmalloc ( sizeof ( * info ) , GFP_KERNEL ) ;
if ( ! info ) {
printk ( KERN_DEBUG " snd_pcm_proc_info_read: cannot malloc \n " ) ;
return ;
}
err = snd_pcm_info ( substream , info ) ;
if ( err < 0 ) {
snd_iprintf ( buffer , " error %d \n " , err ) ;
kfree ( info ) ;
return ;
}
snd_iprintf ( buffer , " card: %d \n " , info - > card ) ;
snd_iprintf ( buffer , " device: %d \n " , info - > device ) ;
snd_iprintf ( buffer , " subdevice: %d \n " , info - > subdevice ) ;
snd_iprintf ( buffer , " stream: %s \n " , snd_pcm_stream_name ( info - > stream ) ) ;
snd_iprintf ( buffer , " id: %s \n " , info - > id ) ;
snd_iprintf ( buffer , " name: %s \n " , info - > name ) ;
snd_iprintf ( buffer , " subname: %s \n " , info - > subname ) ;
snd_iprintf ( buffer , " class: %d \n " , info - > dev_class ) ;
snd_iprintf ( buffer , " subclass: %d \n " , info - > dev_subclass ) ;
snd_iprintf ( buffer , " subdevices_count: %d \n " , info - > subdevices_count ) ;
snd_iprintf ( buffer , " subdevices_avail: %d \n " , info - > subdevices_avail ) ;
kfree ( info ) ;
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_stream_proc_info_read ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
snd_pcm_proc_info_read ( ( ( struct snd_pcm_str * ) entry - > private_data ) - > substream ,
buffer ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_substream_proc_info_read ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-04-17 02:20:36 +04:00
{
2010-09-05 05:52:54 +04:00
snd_pcm_proc_info_read ( entry - > private_data , buffer ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_substream_proc_hw_params_read ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream = entry - > private_data ;
2010-09-17 01:06:50 +04:00
struct snd_pcm_runtime * runtime ;
mutex_lock ( & substream - > pcm - > open_mutex ) ;
runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
if ( ! runtime ) {
snd_iprintf ( buffer , " closed \n " ) ;
2010-09-17 01:06:50 +04:00
goto unlock ;
2005-04-17 02:20:36 +04:00
}
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN ) {
snd_iprintf ( buffer , " no setup \n " ) ;
2010-09-17 01:06:50 +04:00
goto unlock ;
2005-04-17 02:20:36 +04:00
}
snd_iprintf ( buffer , " access: %s \n " , snd_pcm_access_name ( runtime - > access ) ) ;
snd_iprintf ( buffer , " format: %s \n " , snd_pcm_format_name ( runtime - > format ) ) ;
snd_iprintf ( buffer , " subformat: %s \n " , snd_pcm_subformat_name ( runtime - > subformat ) ) ;
snd_iprintf ( buffer , " channels: %u \n " , runtime - > channels ) ;
snd_iprintf ( buffer , " rate: %u (%u/%u) \n " , runtime - > rate , runtime - > rate_num , runtime - > rate_den ) ;
snd_iprintf ( buffer , " period_size: %lu \n " , runtime - > period_size ) ;
snd_iprintf ( buffer , " buffer_size: %lu \n " , runtime - > buffer_size ) ;
# if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
if ( substream - > oss . oss ) {
snd_iprintf ( buffer , " OSS format: %s \n " , snd_pcm_oss_format_name ( runtime - > oss . format ) ) ;
snd_iprintf ( buffer , " OSS channels: %u \n " , runtime - > oss . channels ) ;
snd_iprintf ( buffer , " OSS rate: %u \n " , runtime - > oss . rate ) ;
snd_iprintf ( buffer , " OSS period bytes: %lu \n " , ( unsigned long ) runtime - > oss . period_bytes ) ;
snd_iprintf ( buffer , " OSS periods: %u \n " , runtime - > oss . periods ) ;
snd_iprintf ( buffer , " OSS period frames: %lu \n " , ( unsigned long ) runtime - > oss . period_frames ) ;
}
# endif
2010-09-17 01:06:50 +04:00
unlock :
mutex_unlock ( & substream - > pcm - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_substream_proc_sw_params_read ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream = entry - > private_data ;
2010-09-17 01:06:50 +04:00
struct snd_pcm_runtime * runtime ;
mutex_lock ( & substream - > pcm - > open_mutex ) ;
runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
if ( ! runtime ) {
snd_iprintf ( buffer , " closed \n " ) ;
2010-09-17 01:06:50 +04:00
goto unlock ;
2005-04-17 02:20:36 +04:00
}
if ( runtime - > status - > state = = SNDRV_PCM_STATE_OPEN ) {
snd_iprintf ( buffer , " no setup \n " ) ;
2010-09-17 01:06:50 +04:00
goto unlock ;
2005-04-17 02:20:36 +04:00
}
snd_iprintf ( buffer , " tstamp_mode: %s \n " , snd_pcm_tstamp_mode_name ( runtime - > tstamp_mode ) ) ;
snd_iprintf ( buffer , " period_step: %u \n " , runtime - > period_step ) ;
snd_iprintf ( buffer , " avail_min: %lu \n " , runtime - > control - > avail_min ) ;
snd_iprintf ( buffer , " start_threshold: %lu \n " , runtime - > start_threshold ) ;
snd_iprintf ( buffer , " stop_threshold: %lu \n " , runtime - > stop_threshold ) ;
snd_iprintf ( buffer , " silence_threshold: %lu \n " , runtime - > silence_threshold ) ;
snd_iprintf ( buffer , " silence_size: %lu \n " , runtime - > silence_size ) ;
snd_iprintf ( buffer , " boundary: %lu \n " , runtime - > boundary ) ;
2010-09-17 01:06:50 +04:00
unlock :
mutex_unlock ( & substream - > pcm - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_substream_proc_status_read ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream = entry - > private_data ;
2010-09-17 01:06:50 +04:00
struct snd_pcm_runtime * runtime ;
2005-11-17 15:59:38 +03:00
struct snd_pcm_status status ;
2005-04-17 02:20:36 +04:00
int err ;
2010-09-17 01:06:50 +04:00
mutex_lock ( & substream - > pcm - > open_mutex ) ;
runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
if ( ! runtime ) {
snd_iprintf ( buffer , " closed \n " ) ;
2010-09-17 01:06:50 +04:00
goto unlock ;
2005-04-17 02:20:36 +04:00
}
memset ( & status , 0 , sizeof ( status ) ) ;
err = snd_pcm_status ( substream , & status ) ;
if ( err < 0 ) {
snd_iprintf ( buffer , " error %d \n " , err ) ;
2010-09-17 01:06:50 +04:00
goto unlock ;
2005-04-17 02:20:36 +04:00
}
snd_iprintf ( buffer , " state: %s \n " , snd_pcm_state_name ( status . state ) ) ;
2009-11-10 12:13:30 +03:00
snd_iprintf ( buffer , " owner_pid : %d \n " , pid_vnr ( substream - > pid ) ) ;
2005-04-17 02:20:36 +04:00
snd_iprintf ( buffer , " trigger_time: %ld.%09ld \n " ,
status . trigger_tstamp . tv_sec , status . trigger_tstamp . tv_nsec ) ;
snd_iprintf ( buffer , " tstamp : %ld.%09ld \n " ,
status . tstamp . tv_sec , status . tstamp . tv_nsec ) ;
snd_iprintf ( buffer , " delay : %ld \n " , status . delay ) ;
snd_iprintf ( buffer , " avail : %ld \n " , status . avail ) ;
snd_iprintf ( buffer , " avail_max : %ld \n " , status . avail_max ) ;
snd_iprintf ( buffer , " ----- \n " ) ;
snd_iprintf ( buffer , " hw_ptr : %ld \n " , runtime - > status - > hw_ptr ) ;
snd_iprintf ( buffer , " appl_ptr : %ld \n " , runtime - > control - > appl_ptr ) ;
2010-09-17 01:06:50 +04:00
unlock :
mutex_unlock ( & substream - > pcm - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2006-04-24 23:57:16 +04:00
# ifdef CONFIG_SND_PCM_XRUN_DEBUG
2005-11-17 15:59:38 +03:00
static void snd_pcm_xrun_debug_read ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_str * pstr = entry - > private_data ;
2005-04-17 02:20:36 +04:00
snd_iprintf ( buffer , " %d \n " , pstr - > xrun_debug ) ;
}
2005-11-17 15:59:38 +03:00
static void snd_pcm_xrun_debug_write ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_str * pstr = entry - > private_data ;
2005-04-17 02:20:36 +04:00
char line [ 64 ] ;
if ( ! snd_info_get_line ( buffer , line , sizeof ( line ) ) )
pstr - > xrun_debug = simple_strtoul ( line , NULL , 10 ) ;
}
# endif
2005-11-17 15:59:38 +03:00
static int snd_pcm_stream_proc_init ( struct snd_pcm_str * pstr )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm * pcm = pstr - > pcm ;
struct snd_info_entry * entry ;
2005-04-17 02:20:36 +04:00
char name [ 16 ] ;
sprintf ( name , " pcm%i%c " , pcm - > device ,
pstr - > stream = = SNDRV_PCM_STREAM_PLAYBACK ? ' p ' : ' c ' ) ;
if ( ( entry = snd_info_create_card_entry ( pcm - > card , name , pcm - > card - > proc_root ) ) = = NULL )
return - ENOMEM ;
entry - > mode = S_IFDIR | S_IRUGO | S_IXUGO ;
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
return - ENOMEM ;
}
pstr - > proc_root = entry ;
if ( ( entry = snd_info_create_card_entry ( pcm - > card , " info " , pstr - > proc_root ) ) ! = NULL ) {
2006-04-28 17:13:41 +04:00
snd_info_set_text_ops ( entry , pstr , snd_pcm_stream_proc_info_read ) ;
2005-04-17 02:20:36 +04:00
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
entry = NULL ;
}
}
pstr - > proc_info_entry = entry ;
2006-04-24 23:57:16 +04:00
# ifdef CONFIG_SND_PCM_XRUN_DEBUG
2005-11-17 15:59:38 +03:00
if ( ( entry = snd_info_create_card_entry ( pcm - > card , " xrun_debug " ,
pstr - > proc_root ) ) ! = NULL ) {
2005-04-17 02:20:36 +04:00
entry - > c . text . read = snd_pcm_xrun_debug_read ;
entry - > c . text . write = snd_pcm_xrun_debug_write ;
2005-04-12 18:27:28 +04:00
entry - > mode | = S_IWUSR ;
2005-04-17 02:20:36 +04:00
entry - > private_data = pstr ;
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
entry = NULL ;
}
}
pstr - > proc_xrun_debug_entry = entry ;
# endif
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_stream_proc_done ( struct snd_pcm_str * pstr )
2005-04-17 02:20:36 +04:00
{
2006-04-24 23:57:16 +04:00
# ifdef CONFIG_SND_PCM_XRUN_DEBUG
2006-06-23 16:37:59 +04:00
snd_info_free_entry ( pstr - > proc_xrun_debug_entry ) ;
pstr - > proc_xrun_debug_entry = NULL ;
2005-04-17 02:20:36 +04:00
# endif
2006-06-23 16:37:59 +04:00
snd_info_free_entry ( pstr - > proc_info_entry ) ;
pstr - > proc_info_entry = NULL ;
snd_info_free_entry ( pstr - > proc_root ) ;
pstr - > proc_root = NULL ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_substream_proc_init ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_info_entry * entry ;
struct snd_card * card ;
2005-04-17 02:20:36 +04:00
char name [ 16 ] ;
card = substream - > pcm - > card ;
sprintf ( name , " sub%i " , substream - > number ) ;
if ( ( entry = snd_info_create_card_entry ( card , name , substream - > pstr - > proc_root ) ) = = NULL )
return - ENOMEM ;
entry - > mode = S_IFDIR | S_IRUGO | S_IXUGO ;
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
return - ENOMEM ;
}
substream - > proc_root = entry ;
if ( ( entry = snd_info_create_card_entry ( card , " info " , substream - > proc_root ) ) ! = NULL ) {
2006-04-28 17:13:41 +04:00
snd_info_set_text_ops ( entry , substream ,
snd_pcm_substream_proc_info_read ) ;
2005-04-17 02:20:36 +04:00
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
entry = NULL ;
}
}
substream - > proc_info_entry = entry ;
if ( ( entry = snd_info_create_card_entry ( card , " hw_params " , substream - > proc_root ) ) ! = NULL ) {
2006-04-28 17:13:41 +04:00
snd_info_set_text_ops ( entry , substream ,
snd_pcm_substream_proc_hw_params_read ) ;
2005-04-17 02:20:36 +04:00
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
entry = NULL ;
}
}
substream - > proc_hw_params_entry = entry ;
if ( ( entry = snd_info_create_card_entry ( card , " sw_params " , substream - > proc_root ) ) ! = NULL ) {
2006-04-28 17:13:41 +04:00
snd_info_set_text_ops ( entry , substream ,
snd_pcm_substream_proc_sw_params_read ) ;
2005-04-17 02:20:36 +04:00
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
entry = NULL ;
}
}
substream - > proc_sw_params_entry = entry ;
if ( ( entry = snd_info_create_card_entry ( card , " status " , substream - > proc_root ) ) ! = NULL ) {
2006-04-28 17:13:41 +04:00
snd_info_set_text_ops ( entry , substream ,
snd_pcm_substream_proc_status_read ) ;
2005-04-17 02:20:36 +04:00
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
entry = NULL ;
}
}
substream - > proc_status_entry = entry ;
return 0 ;
}
2006-06-23 16:37:59 +04:00
2005-11-17 15:59:38 +03:00
static int snd_pcm_substream_proc_done ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2006-06-23 16:37:59 +04:00
snd_info_free_entry ( substream - > proc_info_entry ) ;
substream - > proc_info_entry = NULL ;
snd_info_free_entry ( substream - > proc_hw_params_entry ) ;
substream - > proc_hw_params_entry = NULL ;
snd_info_free_entry ( substream - > proc_sw_params_entry ) ;
substream - > proc_sw_params_entry = NULL ;
snd_info_free_entry ( substream - > proc_status_entry ) ;
substream - > proc_status_entry = NULL ;
snd_info_free_entry ( substream - > proc_root ) ;
substream - > proc_root = NULL ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-04-25 14:56:04 +04:00
# else /* !CONFIG_SND_VERBOSE_PROCFS */
2005-12-01 12:42:42 +03:00
static inline int snd_pcm_stream_proc_init ( struct snd_pcm_str * pstr ) { return 0 ; }
static inline int snd_pcm_stream_proc_done ( struct snd_pcm_str * pstr ) { return 0 ; }
static inline int snd_pcm_substream_proc_init ( struct snd_pcm_substream * substream ) { return 0 ; }
static inline int snd_pcm_substream_proc_done ( struct snd_pcm_substream * substream ) { return 0 ; }
2006-04-25 14:56:04 +04:00
# endif /* CONFIG_SND_VERBOSE_PROCFS */
2005-04-17 02:20:36 +04:00
/**
* snd_pcm_new_stream - create a new PCM stream
* @ pcm : the pcm instance
* @ stream : the stream direction , SNDRV_PCM_STREAM_XXX
* @ substream_count : the number of substreams
*
* Creates a new stream for the pcm .
* The corresponding stream on the pcm must have been empty before
* calling this , i . e . zero must be given to the argument of
* snd_pcm_new ( ) .
*
* Returns zero if successful , or a negative error code on failure .
*/
2005-11-17 15:59:38 +03:00
int snd_pcm_new_stream ( struct snd_pcm * pcm , int stream , int substream_count )
2005-04-17 02:20:36 +04:00
{
int idx , err ;
2005-11-17 15:59:38 +03:00
struct snd_pcm_str * pstr = & pcm - > streams [ stream ] ;
struct snd_pcm_substream * substream , * prev ;
2005-04-17 02:20:36 +04:00
# if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
2006-01-16 18:29:08 +03:00
mutex_init ( & pstr - > oss . setup_mutex ) ;
2005-04-17 02:20:36 +04:00
# endif
pstr - > stream = stream ;
pstr - > pcm = pcm ;
pstr - > substream_count = substream_count ;
2012-02-09 00:33:31 +04:00
if ( substream_count > 0 & & ! pcm - > internal ) {
2005-04-17 02:20:36 +04:00
err = snd_pcm_stream_proc_init ( pstr ) ;
2005-11-17 19:44:01 +03:00
if ( err < 0 ) {
snd_printk ( KERN_ERR " Error in snd_pcm_stream_proc_init \n " ) ;
2005-04-17 02:20:36 +04:00
return err ;
2005-11-17 19:44:01 +03:00
}
2005-04-17 02:20:36 +04:00
}
prev = NULL ;
for ( idx = 0 , prev = NULL ; idx < substream_count ; idx + + ) {
2005-09-09 16:20:23 +04:00
substream = kzalloc ( sizeof ( * substream ) , GFP_KERNEL ) ;
2005-11-17 19:44:01 +03:00
if ( substream = = NULL ) {
snd_printk ( KERN_ERR " Cannot allocate PCM substream \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2005-11-17 19:44:01 +03:00
}
2005-04-17 02:20:36 +04:00
substream - > pcm = pcm ;
substream - > pstr = pstr ;
substream - > number = idx ;
substream - > stream = stream ;
sprintf ( substream - > name , " subdevice #%i " , idx ) ;
substream - > buffer_bytes_max = UINT_MAX ;
if ( prev = = NULL )
pstr - > substream = substream ;
else
prev - > next = substream ;
2012-02-09 00:33:31 +04:00
if ( ! pcm - > internal ) {
err = snd_pcm_substream_proc_init ( substream ) ;
if ( err < 0 ) {
snd_printk ( KERN_ERR " Error in snd_pcm_stream_proc_init \n " ) ;
if ( prev = = NULL )
pstr - > substream = NULL ;
else
prev - > next = NULL ;
kfree ( substream ) ;
return err ;
}
2005-04-17 02:20:36 +04:00
}
substream - > group = & substream - > self_group ;
spin_lock_init ( & substream - > self_group . lock ) ;
INIT_LIST_HEAD ( & substream - > self_group . substreams ) ;
list_add_tail ( & substream - > link_list , & substream - > self_group . substreams ) ;
2006-04-28 17:13:41 +04:00
atomic_set ( & substream - > mmap_count , 0 ) ;
2005-04-17 02:20:36 +04:00
prev = substream ;
}
return 0 ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_new_stream ) ;
2012-02-09 00:33:31 +04:00
static int _snd_pcm_new ( struct snd_card * card , const char * id , int device ,
int playback_count , int capture_count , bool internal ,
struct snd_pcm * * rpcm )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm * pcm ;
2005-04-17 02:20:36 +04:00
int err ;
2005-11-17 15:59:38 +03:00
static struct snd_device_ops ops = {
2005-04-17 02:20:36 +04:00
. dev_free = snd_pcm_dev_free ,
. dev_register = snd_pcm_dev_register ,
. dev_disconnect = snd_pcm_dev_disconnect ,
} ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! card ) )
return - ENXIO ;
if ( rpcm )
* rpcm = NULL ;
2005-09-09 16:20:23 +04:00
pcm = kzalloc ( sizeof ( * pcm ) , GFP_KERNEL ) ;
2005-11-17 19:44:01 +03:00
if ( pcm = = NULL ) {
snd_printk ( KERN_ERR " Cannot allocate PCM \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2005-11-17 19:44:01 +03:00
}
2005-04-17 02:20:36 +04:00
pcm - > card = card ;
pcm - > device = device ;
2012-02-09 00:33:31 +04:00
pcm - > internal = internal ;
2005-11-17 19:44:01 +03:00
if ( id )
2005-04-17 02:20:36 +04:00
strlcpy ( pcm - > id , id , sizeof ( pcm - > id ) ) ;
if ( ( err = snd_pcm_new_stream ( pcm , SNDRV_PCM_STREAM_PLAYBACK , playback_count ) ) < 0 ) {
snd_pcm_free ( pcm ) ;
return err ;
}
if ( ( err = snd_pcm_new_stream ( pcm , SNDRV_PCM_STREAM_CAPTURE , capture_count ) ) < 0 ) {
snd_pcm_free ( pcm ) ;
return err ;
}
2006-01-16 18:29:08 +03:00
mutex_init ( & pcm - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
init_waitqueue_head ( & pcm - > open_wait ) ;
if ( ( err = snd_device_new ( card , SNDRV_DEV_PCM , pcm , & ops ) ) < 0 ) {
snd_pcm_free ( pcm ) ;
return err ;
}
2008-08-08 19:09:09 +04:00
if ( rpcm )
* rpcm = pcm ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2012-02-09 00:33:31 +04:00
/**
* snd_pcm_new - create a new PCM instance
* @ card : the card instance
* @ id : the id string
* @ device : the device index ( zero based )
* @ playback_count : the number of substreams for playback
* @ capture_count : the number of substreams for capture
* @ rpcm : the pointer to store the new pcm instance
*
* Creates a new PCM instance .
*
* The pcm operators have to be set afterwards to the new instance
* via snd_pcm_set_ops ( ) .
*
* Returns zero if successful , or a negative error code on failure .
*/
int snd_pcm_new ( struct snd_card * card , const char * id , int device ,
int playback_count , int capture_count , struct snd_pcm * * rpcm )
{
return _snd_pcm_new ( card , id , device , playback_count , capture_count ,
false , rpcm ) ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_new ) ;
2012-02-09 00:33:31 +04:00
/**
* snd_pcm_new_internal - create a new internal PCM instance
* @ card : the card instance
* @ id : the id string
* @ device : the device index ( zero based - shared with normal PCMs )
* @ playback_count : the number of substreams for playback
* @ capture_count : the number of substreams for capture
* @ rpcm : the pointer to store the new pcm instance
*
* Creates a new internal PCM instance with no userspace device or procfs
* entries . This is used by ASoC Back End PCMs in order to create a PCM that
* will only be used internally by kernel drivers . i . e . it cannot be opened
* by userspace . It provides existing ASoC components drivers with a substream
* and access to any private data .
*
* The pcm operators have to be set afterwards to the new instance
* via snd_pcm_set_ops ( ) .
*
* Returns zero if successful , or a negative error code on failure .
*/
int snd_pcm_new_internal ( struct snd_card * card , const char * id , int device ,
int playback_count , int capture_count ,
struct snd_pcm * * rpcm )
{
return _snd_pcm_new ( card , id , device , playback_count , capture_count ,
true , rpcm ) ;
}
EXPORT_SYMBOL ( snd_pcm_new_internal ) ;
2005-11-17 15:59:38 +03:00
static void snd_pcm_free_stream ( struct snd_pcm_str * pstr )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream , * substream_next ;
2005-04-17 02:20:36 +04:00
# if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
2005-11-17 15:59:38 +03:00
struct snd_pcm_oss_setup * setup , * setupn ;
2005-04-17 02:20:36 +04:00
# endif
substream = pstr - > substream ;
while ( substream ) {
substream_next = substream - > next ;
2006-06-23 16:38:23 +04:00
snd_pcm_timer_done ( substream ) ;
2005-04-17 02:20:36 +04:00
snd_pcm_substream_proc_done ( substream ) ;
kfree ( substream ) ;
substream = substream_next ;
}
snd_pcm_stream_proc_done ( pstr ) ;
# if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
for ( setup = pstr - > oss . setup_list ; setup ; setup = setupn ) {
setupn = setup - > next ;
kfree ( setup - > task_name ) ;
kfree ( setup ) ;
}
# endif
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_free ( struct snd_pcm * pcm )
2005-04-17 02:20:36 +04:00
{
2006-06-23 16:38:23 +04:00
struct snd_pcm_notify * notify ;
2008-08-08 19:09:09 +04:00
if ( ! pcm )
return 0 ;
2006-06-23 16:38:23 +04:00
list_for_each_entry ( notify , & snd_pcm_notify_list , list ) {
notify - > n_unregister ( pcm ) ;
}
2005-04-17 02:20:36 +04:00
if ( pcm - > private_free )
pcm - > private_free ( pcm ) ;
snd_pcm_lib_preallocate_free_for_all ( pcm ) ;
snd_pcm_free_stream ( & pcm - > streams [ SNDRV_PCM_STREAM_PLAYBACK ] ) ;
snd_pcm_free_stream ( & pcm - > streams [ SNDRV_PCM_STREAM_CAPTURE ] ) ;
kfree ( pcm ) ;
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_dev_free ( struct snd_device * device )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm * pcm = device - > device_data ;
2005-04-17 02:20:36 +04:00
return snd_pcm_free ( pcm ) ;
}
2006-03-27 18:40:49 +04:00
int snd_pcm_attach_substream ( struct snd_pcm * pcm , int stream ,
struct file * file ,
struct snd_pcm_substream * * rsubstream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_str * pstr ;
struct snd_pcm_substream * substream ;
struct snd_pcm_runtime * runtime ;
struct snd_ctl_file * kctl ;
struct snd_card * card ;
2005-04-17 02:20:36 +04:00
int prefer_subdevice = - 1 ;
size_t size ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! pcm | | ! rsubstream ) )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
* rsubstream = NULL ;
pstr = & pcm - > streams [ stream ] ;
2006-03-27 18:40:49 +04:00
if ( pstr - > substream = = NULL | | pstr - > substream_count = = 0 )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
card = pcm - > card ;
2008-09-25 16:51:03 +04:00
read_lock ( & card - > ctl_files_rwlock ) ;
2006-10-05 18:02:22 +04:00
list_for_each_entry ( kctl , & card - > ctl_files , list ) {
2009-11-02 11:35:44 +03:00
if ( kctl - > pid = = task_pid ( current ) ) {
2005-04-17 02:20:36 +04:00
prefer_subdevice = kctl - > prefer_pcm_subdevice ;
2006-08-04 14:57:19 +04:00
if ( prefer_subdevice ! = - 1 )
break ;
2005-04-17 02:20:36 +04:00
}
}
2008-09-25 16:51:03 +04:00
read_unlock ( & card - > ctl_files_rwlock ) ;
2005-04-17 02:20:36 +04:00
switch ( stream ) {
case SNDRV_PCM_STREAM_PLAYBACK :
if ( pcm - > info_flags & SNDRV_PCM_INFO_HALF_DUPLEX ) {
for ( substream = pcm - > streams [ SNDRV_PCM_STREAM_CAPTURE ] . substream ; substream ; substream = substream - > next ) {
if ( SUBSTREAM_BUSY ( substream ) )
return - EAGAIN ;
}
}
break ;
case SNDRV_PCM_STREAM_CAPTURE :
if ( pcm - > info_flags & SNDRV_PCM_INFO_HALF_DUPLEX ) {
for ( substream = pcm - > streams [ SNDRV_PCM_STREAM_PLAYBACK ] . substream ; substream ; substream = substream - > next ) {
if ( SUBSTREAM_BUSY ( substream ) )
return - EAGAIN ;
}
}
break ;
default :
return - EINVAL ;
}
2006-04-28 17:13:41 +04:00
if ( file - > f_flags & O_APPEND ) {
if ( prefer_subdevice < 0 ) {
if ( pstr - > substream_count > 1 )
return - EINVAL ; /* must be unique */
substream = pstr - > substream ;
} else {
for ( substream = pstr - > substream ; substream ;
substream = substream - > next )
if ( substream - > number = = prefer_subdevice )
break ;
}
if ( ! substream )
return - ENODEV ;
if ( ! SUBSTREAM_BUSY ( substream ) )
return - EBADFD ;
substream - > ref_count + + ;
* rsubstream = substream ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
if ( prefer_subdevice > = 0 ) {
for ( substream = pstr - > substream ; substream ; substream = substream - > next )
if ( ! SUBSTREAM_BUSY ( substream ) & & substream - > number = = prefer_subdevice )
goto __ok ;
}
for ( substream = pstr - > substream ; substream ; substream = substream - > next )
if ( ! SUBSTREAM_BUSY ( substream ) )
break ;
__ok :
if ( substream = = NULL )
return - EAGAIN ;
2005-09-09 16:20:23 +04:00
runtime = kzalloc ( sizeof ( * runtime ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( runtime = = NULL )
return - ENOMEM ;
2005-11-17 15:59:38 +03:00
size = PAGE_ALIGN ( sizeof ( struct snd_pcm_mmap_status ) ) ;
2005-04-17 02:20:36 +04:00
runtime - > status = snd_malloc_pages ( size , GFP_KERNEL ) ;
if ( runtime - > status = = NULL ) {
kfree ( runtime ) ;
return - ENOMEM ;
}
memset ( ( void * ) runtime - > status , 0 , size ) ;
2005-11-17 15:59:38 +03:00
size = PAGE_ALIGN ( sizeof ( struct snd_pcm_mmap_control ) ) ;
2005-04-17 02:20:36 +04:00
runtime - > control = snd_malloc_pages ( size , GFP_KERNEL ) ;
if ( runtime - > control = = NULL ) {
2005-11-17 15:59:38 +03:00
snd_free_pages ( ( void * ) runtime - > status ,
PAGE_ALIGN ( sizeof ( struct snd_pcm_mmap_status ) ) ) ;
2005-04-17 02:20:36 +04:00
kfree ( runtime ) ;
return - ENOMEM ;
}
memset ( ( void * ) runtime - > control , 0 , size ) ;
init_waitqueue_head ( & runtime - > sleep ) ;
2010-01-21 12:32:15 +03:00
init_waitqueue_head ( & runtime - > tsleep ) ;
2005-04-17 02:20:36 +04:00
runtime - > status - > state = SNDRV_PCM_STATE_OPEN ;
substream - > runtime = runtime ;
substream - > private_data = pcm - > private_data ;
2006-04-28 17:13:41 +04:00
substream - > ref_count = 1 ;
substream - > f_flags = file - > f_flags ;
2009-11-10 12:13:30 +03:00
substream - > pid = get_pid ( task_pid ( current ) ) ;
2005-04-17 02:20:36 +04:00
pstr - > substream_opened + + ;
* rsubstream = substream ;
return 0 ;
}
2006-03-27 18:40:49 +04:00
void snd_pcm_detach_substream ( 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 ;
2006-04-28 17:13:41 +04:00
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 - > private_free ! = NULL )
runtime - > private_free ( runtime ) ;
2005-11-17 15:59:38 +03:00
snd_free_pages ( ( void * ) runtime - > status ,
PAGE_ALIGN ( sizeof ( struct snd_pcm_mmap_status ) ) ) ;
snd_free_pages ( ( void * ) runtime - > control ,
PAGE_ALIGN ( sizeof ( struct snd_pcm_mmap_control ) ) ) ;
2005-04-17 02:20:36 +04:00
kfree ( runtime - > hw_constraints . rules ) ;
2009-12-20 13:47:57 +03:00
# ifdef CONFIG_SND_PCM_XRUN_DEBUG
if ( runtime - > hwptr_log )
kfree ( runtime - > hwptr_log ) ;
# endif
2005-04-17 02:20:36 +04:00
kfree ( runtime ) ;
substream - > runtime = NULL ;
2009-11-10 12:13:30 +03:00
put_pid ( substream - > pid ) ;
substream - > pid = NULL ;
2005-04-17 02:20:36 +04:00
substream - > pstr - > substream_opened - - ;
}
2006-08-08 09:19:37 +04:00
static ssize_t show_pcm_class ( struct device * dev ,
struct device_attribute * attr , char * buf )
2006-09-06 16:27:46 +04:00
{
struct snd_pcm * pcm ;
const char * str ;
static const char * strs [ SNDRV_PCM_CLASS_LAST + 1 ] = {
[ SNDRV_PCM_CLASS_GENERIC ] = " generic " ,
[ SNDRV_PCM_CLASS_MULTI ] = " multi " ,
[ SNDRV_PCM_CLASS_MODEM ] = " modem " ,
[ SNDRV_PCM_CLASS_DIGITIZER ] = " digitizer " ,
} ;
2006-08-08 09:19:37 +04:00
if ( ! ( pcm = dev_get_drvdata ( dev ) ) | |
2006-09-06 16:27:46 +04:00
pcm - > dev_class > SNDRV_PCM_CLASS_LAST )
str = " none " ;
else
str = strs [ pcm - > dev_class ] ;
return snprintf ( buf , PAGE_SIZE , " %s \n " , str ) ;
}
2006-08-08 09:19:37 +04:00
static struct device_attribute pcm_attrs =
2006-09-06 16:27:46 +04:00
__ATTR ( pcm_class , S_IRUGO , show_pcm_class , NULL ) ;
2005-11-17 15:59:38 +03:00
static int snd_pcm_dev_register ( struct snd_device * device )
2005-04-17 02:20:36 +04:00
{
2005-11-20 16:06:59 +03:00
int cidx , err ;
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream ;
2006-10-05 18:02:22 +04:00
struct snd_pcm_notify * notify ;
2005-04-17 02:20:36 +04:00
char str [ 16 ] ;
2009-10-17 10:33:22 +04:00
struct snd_pcm * pcm ;
2006-10-05 17:06:34 +04:00
struct device * dev ;
2005-04-17 02:20:36 +04:00
2009-10-17 10:33:22 +04:00
if ( snd_BUG_ON ( ! device | | ! device - > device_data ) )
2008-08-08 19:09:09 +04:00
return - ENXIO ;
2009-10-17 10:33:22 +04:00
pcm = device - > device_data ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2008-07-30 15:46:40 +04:00
err = snd_pcm_add ( pcm ) ;
if ( err ) {
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2008-07-30 15:46:40 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
for ( cidx = 0 ; cidx < 2 ; cidx + + ) {
int devtype = - 1 ;
2012-02-09 00:33:31 +04:00
if ( pcm - > streams [ cidx ] . substream = = NULL | | pcm - > internal )
2005-04-17 02:20:36 +04:00
continue ;
switch ( cidx ) {
case SNDRV_PCM_STREAM_PLAYBACK :
sprintf ( str , " pcmC%iD%ip " , pcm - > card - > number , pcm - > device ) ;
devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK ;
break ;
case SNDRV_PCM_STREAM_CAPTURE :
sprintf ( str , " pcmC%iD%ic " , pcm - > card - > number , pcm - > device ) ;
devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE ;
break ;
}
2006-10-05 17:06:34 +04:00
/* device pointer to use, pcm->dev takes precedence if
* it is assigned , otherwise fall back to card ' s device
* if possible */
dev = pcm - > dev ;
if ( ! dev )
2007-02-09 18:25:48 +03:00
dev = snd_card_get_device_link ( pcm - > card ) ;
2006-10-05 17:06:34 +04:00
/* register pcm */
err = snd_register_device_for_dev ( devtype , pcm - > card ,
pcm - > device ,
& snd_pcm_f_ops [ cidx ] ,
pcm , str , dev ) ;
if ( err < 0 ) {
2005-11-20 16:06:59 +03:00
list_del ( & pcm - > list ) ;
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2006-09-06 16:27:46 +04:00
snd_add_device_sysfs_file ( devtype , pcm - > card , pcm - > device ,
& pcm_attrs ) ;
2005-04-17 02:20:36 +04:00
for ( substream = pcm - > streams [ cidx ] . substream ; substream ; substream = substream - > next )
snd_pcm_timer_init ( substream ) ;
}
2006-10-05 18:02:22 +04:00
list_for_each_entry ( notify , & snd_pcm_notify_list , list )
2005-04-17 02:20:36 +04:00
notify - > n_register ( pcm ) ;
2006-10-05 18:02:22 +04:00
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_dev_disconnect ( struct snd_device * device )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm * pcm = device - > device_data ;
2006-06-23 16:38:23 +04:00
struct snd_pcm_notify * notify ;
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream ;
2006-06-23 16:38:23 +04:00
int cidx , devtype ;
2005-04-17 02:20:36 +04:00
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2006-06-23 16:38:23 +04:00
if ( list_empty ( & pcm - > list ) )
goto unlock ;
2012-10-12 17:07:34 +04:00
mutex_lock ( & pcm - > open_mutex ) ;
2012-10-16 18:43:39 +04:00
wake_up ( & pcm - > open_wait ) ;
2005-11-20 16:06:59 +03:00
list_del_init ( & pcm - > list ) ;
2005-04-17 02:20:36 +04:00
for ( cidx = 0 ; cidx < 2 ; cidx + + )
2012-10-12 17:07:34 +04:00
for ( substream = pcm - > streams [ cidx ] . substream ; substream ; substream = substream - > next ) {
snd_pcm_stream_lock_irq ( substream ) ;
2012-10-16 18:43:39 +04:00
if ( substream - > runtime ) {
2005-04-17 02:20:36 +04:00
substream - > runtime - > status - > state = SNDRV_PCM_STATE_DISCONNECTED ;
2012-10-16 18:43:39 +04:00
wake_up ( & substream - > runtime - > sleep ) ;
wake_up ( & substream - > runtime - > tsleep ) ;
}
2012-10-12 17:07:34 +04:00
snd_pcm_stream_unlock_irq ( substream ) ;
}
2006-06-23 16:38:23 +04:00
list_for_each_entry ( notify , & snd_pcm_notify_list , list ) {
2005-04-17 02:20:36 +04:00
notify - > n_disconnect ( pcm ) ;
}
for ( cidx = 0 ; cidx < 2 ; cidx + + ) {
devtype = - 1 ;
switch ( cidx ) {
case SNDRV_PCM_STREAM_PLAYBACK :
devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK ;
break ;
case SNDRV_PCM_STREAM_CAPTURE :
devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE ;
break ;
}
snd_unregister_device ( devtype , pcm - > card , pcm - > device ) ;
2012-07-27 20:27:00 +04:00
if ( pcm - > streams [ cidx ] . chmap_kctl ) {
snd_ctl_remove ( pcm - > card , pcm - > streams [ cidx ] . chmap_kctl ) ;
pcm - > streams [ cidx ] . chmap_kctl = NULL ;
}
2005-04-17 02:20:36 +04:00
}
2012-10-12 17:07:34 +04:00
mutex_unlock ( & pcm - > open_mutex ) ;
2006-06-23 16:38:23 +04:00
unlock :
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2006-06-23 16:38:23 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-11-17 15:59:38 +03:00
int snd_pcm_notify ( struct snd_pcm_notify * notify , int nfree )
2005-04-17 02:20:36 +04:00
{
2006-10-05 18:02:22 +04:00
struct snd_pcm * pcm ;
2005-04-17 02:20:36 +04:00
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! notify | |
! notify - > n_register | |
! notify - > n_unregister | |
! notify - > n_disconnect ) )
return - EINVAL ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( nfree ) {
list_del ( & notify - > list ) ;
2006-10-05 18:02:22 +04:00
list_for_each_entry ( pcm , & snd_pcm_devices , list )
notify - > n_unregister ( pcm ) ;
2005-04-17 02:20:36 +04:00
} else {
list_add_tail ( & notify - > list , & snd_pcm_notify_list ) ;
2006-10-05 18:02:22 +04:00
list_for_each_entry ( pcm , & snd_pcm_devices , list )
notify - > n_register ( pcm ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-04-28 17:13:40 +04:00
EXPORT_SYMBOL ( snd_pcm_notify ) ;
2005-12-01 12:42:42 +03:00
# ifdef CONFIG_PROC_FS
2005-04-17 02:20:36 +04:00
/*
* Info interface
*/
2005-11-17 15:59:38 +03:00
static void snd_pcm_proc_read ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm * pcm ;
2005-04-17 02:20:36 +04:00
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2006-10-05 18:02:22 +04:00
list_for_each_entry ( pcm , & snd_pcm_devices , list ) {
2005-11-20 16:06:59 +03:00
snd_iprintf ( buffer , " %02i-%02i: %s : %s " ,
pcm - > card - > number , pcm - > device , pcm - > id , pcm - > name ) ;
2005-04-17 02:20:36 +04:00
if ( pcm - > streams [ SNDRV_PCM_STREAM_PLAYBACK ] . substream )
2005-11-17 15:59:38 +03:00
snd_iprintf ( buffer , " : playback %i " ,
pcm - > streams [ SNDRV_PCM_STREAM_PLAYBACK ] . substream_count ) ;
2005-04-17 02:20:36 +04:00
if ( pcm - > streams [ SNDRV_PCM_STREAM_CAPTURE ] . substream )
2005-11-17 15:59:38 +03:00
snd_iprintf ( buffer , " : capture %i " ,
pcm - > streams [ SNDRV_PCM_STREAM_CAPTURE ] . substream_count ) ;
2005-04-17 02:20:36 +04:00
snd_iprintf ( buffer , " \n " ) ;
}
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-17 19:14:51 +04:00
static struct snd_info_entry * snd_pcm_proc_entry ;
2005-04-17 02:20:36 +04:00
2005-12-01 12:42:42 +03:00
static void snd_pcm_proc_init ( void )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_info_entry * entry ;
2005-04-17 02:20:36 +04:00
if ( ( entry = snd_info_create_module_entry ( THIS_MODULE , " pcm " , NULL ) ) ! = NULL ) {
2006-04-28 17:13:41 +04:00
snd_info_set_text_ops ( entry , NULL , snd_pcm_proc_read ) ;
2005-04-17 02:20:36 +04:00
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
entry = NULL ;
}
}
snd_pcm_proc_entry = entry ;
2005-12-01 12:42:42 +03:00
}
static void snd_pcm_proc_done ( void )
{
2006-06-23 16:37:59 +04:00
snd_info_free_entry ( snd_pcm_proc_entry ) ;
2005-12-01 12:42:42 +03:00
}
# else /* !CONFIG_PROC_FS */
# define snd_pcm_proc_init()
# define snd_pcm_proc_done()
# endif /* CONFIG_PROC_FS */
/*
* ENTRY functions
*/
static int __init alsa_pcm_init ( void )
{
snd_ctl_register_ioctl ( snd_pcm_control_ioctl ) ;
snd_ctl_register_ioctl_compat ( snd_pcm_control_ioctl ) ;
snd_pcm_proc_init ( ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void __exit alsa_pcm_exit ( void )
{
snd_ctl_unregister_ioctl ( snd_pcm_control_ioctl ) ;
snd_ctl_unregister_ioctl_compat ( snd_pcm_control_ioctl ) ;
2005-12-01 12:42:42 +03:00
snd_pcm_proc_done ( ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( alsa_pcm_init )
module_exit ( alsa_pcm_exit )