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>
2018-04-02 23:41:43 +03:00
# include <sound/timer.h>
2005-04-17 02:20:36 +04:00
# include <sound/control.h>
# include <sound/info.h>
2017-05-26 03:30:46 +03:00
# include "pcm_local.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 ) ;
2006-01-16 18:29:08 +03:00
static DEFINE_MUTEX ( register_mutex ) ;
2017-05-12 12:35:17 +03:00
# if IS_ENABLED(CONFIG_SND_PCM_OSS)
static LIST_HEAD ( snd_pcm_notify_list ) ;
# endif
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 ;
2015-02-20 18:49:04 +03:00
if ( newpcm - > internal )
return 0 ;
2008-07-30 15:46:40 +04:00
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 ;
}
2017-12-05 20:16:55 +03:00
mutex_lock ( & pcm - > open_mutex ) ;
2005-11-20 16:06:59 +03:00
err = snd_pcm_info_user ( substream , info ) ;
2017-12-05 20:16:55 +03:00
mutex_unlock ( & pcm - > open_mutex ) ;
2005-11-20 16:06:59 +03:00
_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 ;
2014-02-19 17:30:29 +04:00
control - > preferred_subdevice [ SND_CTL_SUBDEV_PCM ] = val ;
2005-04-17 02:20:36 +04:00
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 ) ,
2013-04-16 20:01:36 +04:00
FORMAT ( DSD_U8 ) ,
FORMAT ( DSD_U16_LE ) ,
2014-09-05 12:47:56 +04:00
FORMAT ( DSD_U32_LE ) ,
2014-11-21 17:04:46 +03:00
FORMAT ( DSD_U16_BE ) ,
FORMAT ( DSD_U32_BE ) ,
2005-04-17 02:20:36 +04:00
} ;
2014-10-30 17:02:50 +03:00
/**
* snd_pcm_format_name - Return a name string for the given PCM format
* @ format : PCM format
*/
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
}
2014-02-10 12:48:47 +04:00
# if IS_ENABLED(CONFIG_SND_PCM_OSS)
2005-04-17 02:20:36 +04:00
# 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 ) ;
2015-03-10 17:42:14 +03:00
if ( ! info )
2005-04-17 02:20:36 +04:00
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 ) ;
2014-02-10 12:48:47 +04:00
# if IS_ENABLED(CONFIG_SND_PCM_OSS)
2005-04-17 02:20:36 +04:00
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
2014-11-04 16:02:40 +03:00
static void snd_pcm_xrun_injection_write ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
{
struct snd_pcm_substream * substream = entry - > private_data ;
struct snd_pcm_runtime * runtime ;
snd_pcm_stream_lock_irq ( substream ) ;
runtime = substream - > runtime ;
if ( runtime & & runtime - > status - > state = = SNDRV_PCM_STATE_RUNNING )
2018-07-04 15:46:27 +03:00
__snd_pcm_xrun ( substream ) ;
2014-11-04 16:02:40 +03:00
snd_pcm_stream_unlock_irq ( substream ) ;
}
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 ' ) ;
2017-08-23 10:20:29 +03:00
entry = snd_info_create_card_entry ( pcm - > card , name ,
pcm - > card - > proc_root ) ;
if ( ! entry )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2018-05-23 22:20:59 +03:00
entry - > mode = S_IFDIR | 0555 ;
2005-04-17 02:20:36 +04:00
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
return - ENOMEM ;
}
pstr - > proc_root = entry ;
2017-08-23 10:20:29 +03:00
entry = snd_info_create_card_entry ( pcm - > card , " info " , pstr - > proc_root ) ;
if ( entry ) {
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
2017-08-23 10:20:29 +03:00
entry = snd_info_create_card_entry ( pcm - > card , " xrun_debug " ,
pstr - > proc_root ) ;
if ( entry ) {
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 ;
2018-05-23 22:20:59 +03:00
entry - > mode | = 0200 ;
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 ) ;
2017-08-23 10:20:29 +03:00
entry = snd_info_create_card_entry ( card , name ,
substream - > pstr - > proc_root ) ;
if ( ! entry )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2018-05-23 22:20:59 +03:00
entry - > mode = S_IFDIR | 0555 ;
2005-04-17 02:20:36 +04:00
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
return - ENOMEM ;
}
substream - > proc_root = entry ;
2017-08-23 10:20:29 +03:00
entry = snd_info_create_card_entry ( card , " info " , substream - > proc_root ) ;
if ( entry ) {
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 ;
2017-08-23 10:20:29 +03:00
entry = snd_info_create_card_entry ( card , " hw_params " ,
substream - > proc_root ) ;
if ( entry ) {
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 ;
2017-08-23 10:20:29 +03:00
entry = snd_info_create_card_entry ( card , " sw_params " ,
substream - > proc_root ) ;
if ( entry ) {
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 ;
2017-08-24 23:11:07 +03:00
entry = snd_info_create_card_entry ( card , " status " ,
substream - > proc_root ) ;
2017-08-23 10:20:29 +03:00
if ( entry ) {
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 ;
2014-11-04 16:02:40 +03:00
# ifdef CONFIG_SND_PCM_XRUN_DEBUG
entry = snd_info_create_card_entry ( card , " xrun_injection " ,
substream - > proc_root ) ;
if ( entry ) {
entry - > private_data = substream ;
entry - > c . text . read = NULL ;
entry - > c . text . write = snd_pcm_xrun_injection_write ;
2018-05-23 22:20:59 +03:00
entry - > mode = S_IFREG | 0200 ;
2014-11-04 16:02:40 +03:00
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
entry = NULL ;
}
}
substream - > proc_xrun_injection_entry = entry ;
# endif /* CONFIG_SND_PCM_XRUN_DEBUG */
2005-04-17 02:20:36 +04:00
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 ;
2014-11-04 16:02:40 +03:00
# ifdef CONFIG_SND_PCM_XRUN_DEBUG
snd_info_free_entry ( substream - > proc_xrun_injection_entry ) ;
substream - > proc_xrun_injection_entry = NULL ;
# endif
2006-06-23 16:37:59 +04:00
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
2015-01-29 19:32:26 +03:00
static const struct attribute_group * pcm_dev_attr_groups [ ] ;
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 ( ) .
*
2013-03-12 01:05:14 +04:00
* Return : Zero if successful , or a negative error code on failure .
2005-04-17 02:20:36 +04:00
*/
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
2014-02-10 12:48:47 +04:00
# if IS_ENABLED(CONFIG_SND_PCM_OSS)
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 ;
2015-01-29 19:32:26 +03:00
if ( ! substream_count )
return 0 ;
snd_device_initialize ( & pstr - > dev , pcm - > card ) ;
pstr - > dev . groups = pcm_dev_attr_groups ;
dev_set_name ( & pstr - > dev , " pcmC%iD%i%c " , pcm - > card - > number , pcm - > device ,
stream = = SNDRV_PCM_STREAM_PLAYBACK ? ' p ' : ' c ' ) ;
if ( ! 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 ) {
2014-02-04 21:19:48 +04:00
pcm_err ( pcm , " 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 ) ;
2015-03-10 17:42:14 +03:00
if ( ! substream )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
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 ) {
2014-02-04 21:19:48 +04:00
pcm_err ( pcm ,
" Error in snd_pcm_stream_proc_init \n " ) ;
2012-02-09 00:33:31 +04:00
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 ) ;
2014-08-29 17:32:29 +04:00
mutex_init ( & substream - > self_group . mutex ) ;
2005-04-17 02:20:36 +04:00
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 ,
} ;
2017-10-17 12:40:55 +03:00
static struct snd_device_ops internal_ops = {
. dev_free = snd_pcm_dev_free ,
} ;
2005-04-17 02:20:36 +04:00
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 ) ;
2015-03-10 17:42:14 +03:00
if ( ! pcm )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
pcm - > card = card ;
pcm - > device = device ;
2012-02-09 00:33:31 +04:00
pcm - > internal = internal ;
2015-02-20 18:49:04 +03:00
mutex_init ( & pcm - > open_mutex ) ;
init_waitqueue_head ( & pcm - > open_wait ) ;
INIT_LIST_HEAD ( & pcm - > list ) ;
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 ) ) ;
2017-08-23 09:40:37 +03:00
err = snd_pcm_new_stream ( pcm , SNDRV_PCM_STREAM_PLAYBACK ,
playback_count ) ;
if ( err < 0 )
goto free_pcm ;
err = snd_pcm_new_stream ( pcm , SNDRV_PCM_STREAM_CAPTURE , capture_count ) ;
if ( err < 0 )
goto free_pcm ;
2017-10-17 12:40:55 +03:00
err = snd_device_new ( card , SNDRV_DEV_PCM , pcm ,
internal ? & internal_ops : & ops ) ;
2017-08-23 09:40:37 +03:00
if ( err < 0 )
goto free_pcm ;
2008-08-08 19:09:09 +04:00
if ( rpcm )
* rpcm = pcm ;
2005-04-17 02:20:36 +04:00
return 0 ;
2017-08-23 09:40:37 +03:00
free_pcm :
snd_pcm_free ( pcm ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
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 ( ) .
*
2013-03-12 01:05:14 +04:00
* Return : Zero if successful , or a negative error code on failure .
2012-02-09 00:33:31 +04:00
*/
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 ( ) .
*
2013-03-12 01:05:14 +04:00
* Return : Zero if successful , or a negative error code on failure .
2012-02-09 00:33:31 +04:00
*/
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 ) ;
2016-07-08 09:23:43 +03:00
static void free_chmap ( struct snd_pcm_str * pstr )
{
if ( pstr - > chmap_kctl ) {
snd_ctl_remove ( pstr - > pcm - > card , pstr - > chmap_kctl ) ;
pstr - > chmap_kctl = NULL ;
}
}
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 ;
2014-02-10 12:48:47 +04:00
# if IS_ENABLED(CONFIG_SND_PCM_OSS)
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 ) ;
2014-02-10 12:48:47 +04:00
# if IS_ENABLED(CONFIG_SND_PCM_OSS)
2005-04-17 02:20:36 +04:00
for ( setup = pstr - > oss . setup_list ; setup ; setup = setupn ) {
setupn = setup - > next ;
kfree ( setup - > task_name ) ;
kfree ( setup ) ;
}
# endif
2016-07-08 09:23:43 +03:00
free_chmap ( pstr ) ;
2015-01-29 19:32:26 +03:00
if ( pstr - > substream_count )
put_device ( & pstr - > dev ) ;
2005-04-17 02:20:36 +04:00
}
2017-05-12 12:35:17 +03:00
# if IS_ENABLED(CONFIG_SND_PCM_OSS)
# define pcm_call_notify(pcm, call) \
do { \
struct snd_pcm_notify * _notify ; \
list_for_each_entry ( _notify , & snd_pcm_notify_list , list ) \
_notify - > call ( pcm ) ; \
} while ( 0 )
# else
2017-05-18 16:35:54 +03:00
# define pcm_call_notify(pcm, call) do {} while (0)
2017-05-12 12:35:17 +03:00
# 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
{
2008-08-08 19:09:09 +04:00
if ( ! pcm )
return 0 ;
2017-05-12 12:35:17 +03:00
if ( ! pcm - > internal )
pcm_call_notify ( pcm , n_unregister ) ;
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_card * card ;
2014-02-19 17:30:29 +04:00
int prefer_subdevice ;
2005-04-17 02:20:36 +04:00
size_t size ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! pcm | | ! rsubstream ) )
return - ENXIO ;
2015-02-20 18:26:00 +03:00
if ( snd_BUG_ON ( stream ! = SNDRV_PCM_STREAM_PLAYBACK & &
stream ! = SNDRV_PCM_STREAM_CAPTURE ) )
return - EINVAL ;
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 ;
2014-02-19 17:30:29 +04:00
prefer_subdevice = snd_ctl_get_preferred_subdevice ( card , SND_CTL_SUBDEV_PCM ) ;
2005-04-17 02:20:36 +04:00
2015-02-20 18:26:00 +03:00
if ( pcm - > info_flags & SNDRV_PCM_INFO_HALF_DUPLEX ) {
int opposite = ! stream ;
for ( substream = pcm - > streams [ opposite ] . substream ; substream ;
substream = substream - > next ) {
if ( SUBSTREAM_BUSY ( substream ) )
return - EAGAIN ;
2005-04-17 02:20:36 +04:00
}
}
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 ;
}
2015-02-20 18:26:00 +03:00
for ( substream = pstr - > substream ; substream ; substream = substream - > next ) {
if ( ! SUBSTREAM_BUSY ( substream ) & &
( prefer_subdevice = = - 1 | |
substream - > number = = prefer_subdevice ) )
2005-04-17 02:20:36 +04:00
break ;
2015-02-20 18:26:00 +03:00
}
2005-04-17 02:20:36 +04:00
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 ) ;
2018-04-02 23:41:43 +03:00
/* Avoid concurrent access to runtime via PCM timer interface */
if ( substream - > timer )
spin_lock_irq ( & substream - > timer - > lock ) ;
2005-04-17 02:20:36 +04:00
substream - > runtime = NULL ;
2018-04-02 23:41:43 +03:00
if ( substream - > timer )
spin_unlock_irq ( & substream - > timer - > lock ) ;
kfree ( runtime ) ;
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
{
2015-06-23 12:56:22 +03:00
struct snd_pcm_str * pstr = container_of ( dev , struct snd_pcm_str , dev ) ;
struct snd_pcm * pcm = pstr - > pcm ;
2006-09-06 16:27:46 +04:00
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 " ,
} ;
2015-06-23 12:56:22 +03:00
if ( pcm - > dev_class > SNDRV_PCM_CLASS_LAST )
2006-09-06 16:27:46 +04:00
str = " none " ;
else
str = strs [ pcm - > dev_class ] ;
return snprintf ( buf , PAGE_SIZE , " %s \n " , str ) ;
}
2018-05-23 22:20:59 +03:00
static DEVICE_ATTR ( pcm_class , 0444 , show_pcm_class , NULL ) ;
2014-02-25 11:30:50 +04:00
static struct attribute * pcm_dev_attrs [ ] = {
& dev_attr_pcm_class . attr ,
NULL
} ;
2017-06-29 13:32:56 +03:00
static const struct attribute_group pcm_dev_attr_group = {
2014-02-25 11:30:50 +04:00
. attrs = pcm_dev_attrs ,
} ;
static const struct attribute_group * pcm_dev_attr_groups [ ] = {
& pcm_dev_attr_group ,
NULL
} ;
2006-09-06 16:27:46 +04:00
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 ;
2009-10-17 10:33:22 +04:00
struct snd_pcm * pcm ;
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 ;
2015-02-20 18:49:04 +03:00
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 ) ;
2015-02-20 18:49:04 +03:00
if ( err )
goto unlock ;
2005-04-17 02:20:36 +04:00
for ( cidx = 0 ; cidx < 2 ; cidx + + ) {
int devtype = - 1 ;
2015-02-20 18:49:04 +03:00
if ( pcm - > streams [ cidx ] . substream = = NULL )
2005-04-17 02:20:36 +04:00
continue ;
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 ;
}
2006-10-05 17:06:34 +04:00
/* register pcm */
2015-01-30 10:34:58 +03:00
err = snd_register_device ( devtype , pcm - > card , pcm - > device ,
& snd_pcm_f_ops [ cidx ] , pcm ,
& pcm - > streams [ cidx ] . dev ) ;
2006-10-05 17:06:34 +04:00
if ( err < 0 ) {
2015-02-20 18:49:04 +03:00
list_del_init ( & pcm - > list ) ;
goto unlock ;
2005-04-17 02:20:36 +04:00
}
2014-02-25 11:30:50 +04:00
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
2017-05-12 12:35:17 +03:00
pcm_call_notify ( pcm , n_register ) ;
2006-10-05 18:02:22 +04:00
2015-02-20 18:49:04 +03:00
unlock :
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2015-02-20 18:49:04 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}
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 ;
struct snd_pcm_substream * substream ;
2015-01-30 10:34:58 +03:00
int cidx ;
2005-04-17 02:20:36 +04:00
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
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 ) ;
2015-02-20 19:04:08 +03: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 ) {
2017-10-11 12:42:00 +03:00
if ( snd_pcm_running ( substream ) )
snd_pcm_stop ( substream ,
SNDRV_PCM_STATE_DISCONNECTED ) ;
/* to be sure, set the state unconditionally */
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 ) ;
}
2015-02-20 19:04:08 +03:00
}
2017-10-17 12:40:55 +03:00
pcm_call_notify ( pcm , n_disconnect ) ;
2005-04-17 02:20:36 +04:00
for ( cidx = 0 ; cidx < 2 ; cidx + + ) {
2017-10-17 12:40:55 +03:00
snd_unregister_device ( & pcm - > streams [ cidx ] . dev ) ;
2016-07-08 09:23:43 +03:00
free_chmap ( & pcm - > streams [ cidx ] ) ;
2005-04-17 02:20:36 +04:00
}
2012-10-12 17:07:34 +04:00
mutex_unlock ( & pcm - > open_mutex ) ;
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
}
2017-05-12 12:35:17 +03:00
# if IS_ENABLED(CONFIG_SND_PCM_OSS)
2014-10-30 17:02:50 +03:00
/**
* snd_pcm_notify - Add / remove the notify list
* @ notify : PCM notify list
* @ nfree : 0 = register , 1 = unregister
*
* This adds the given notifier to the global list so that the callback is
* called for each registered PCM devices . This exists only for PCM OSS
* emulation , so far .
*/
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 ) ;
2017-05-12 12:35:17 +03:00
# endif /* CONFIG_SND_PCM_OSS */
2006-04-28 17:13:40 +04:00
2015-05-27 14:45:45 +03:00
# ifdef CONFIG_SND_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
2017-08-23 10:20:29 +03:00
entry = snd_info_create_module_entry ( THIS_MODULE , " pcm " , NULL ) ;
if ( entry ) {
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
}
2015-05-27 14:45:45 +03:00
# else /* !CONFIG_SND_PROC_FS */
2005-12-01 12:42:42 +03:00
# define snd_pcm_proc_init()
# define snd_pcm_proc_done()
2015-05-27 14:45:45 +03:00
# endif /* CONFIG_SND_PROC_FS */
2005-12-01 12:42:42 +03:00
/*
* 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 )