2019-05-27 09:55:05 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
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
*/
# 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>
2018-12-13 00:36:28 +03:00
# include <linux/nospec.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 ;
2018-12-13 00:36:28 +03:00
stream = array_index_nospec ( stream , 2 ) ;
2005-04-17 02:20:36 +04:00
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
2020-01-05 17:47:20 +03:00
static const char * const snd_pcm_format_names [ ] = {
2005-04-17 02:20:36 +04:00
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
2022-07-13 13:47:54 +03:00
*
* Return : the format name string
2014-10-30 17:02:50 +03: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
2020-01-05 17:47:20 +03:00
static const char * const snd_pcm_stream_names [ ] = {
2005-12-01 12:42:42 +03:00
STREAM ( PLAYBACK ) ,
STREAM ( CAPTURE ) ,
} ;
2020-01-05 17:47:20 +03:00
static const char * const snd_pcm_state_names [ ] = {
2005-12-01 12:42:42 +03:00
STATE ( OPEN ) ,
STATE ( SETUP ) ,
STATE ( PREPARED ) ,
STATE ( RUNNING ) ,
STATE ( XRUN ) ,
STATE ( DRAINING ) ,
STATE ( PAUSED ) ,
STATE ( SUSPENDED ) ,
} ;
2020-01-05 17:47:20 +03:00
static const char * const snd_pcm_access_names [ ] = {
2005-12-01 12:42:42 +03:00
ACCESS ( MMAP_INTERLEAVED ) ,
ACCESS ( MMAP_NONINTERLEAVED ) ,
ACCESS ( MMAP_COMPLEX ) ,
ACCESS ( RW_INTERLEAVED ) ,
ACCESS ( RW_NONINTERLEAVED ) ,
} ;
2020-01-05 17:47:20 +03:00
static const char * const snd_pcm_subformat_names [ ] = {
2005-04-17 02:20:36 +04:00
SUBFORMAT ( STD ) ,
} ;
2020-01-05 17:47:20 +03:00
static const char * const snd_pcm_tstamp_mode_names [ ] = {
2005-04-17 02:20:36 +04:00
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
}
2022-09-26 16:55:48 +03:00
if ( runtime - > state = = SNDRV_PCM_STATE_OPEN ) {
2005-04-17 02:20:36 +04:00
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
}
2022-09-26 16:55:48 +03:00
if ( runtime - > state = = SNDRV_PCM_STATE_OPEN ) {
2005-04-17 02:20:36 +04:00
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 ;
2018-04-24 15:06:11 +03:00
struct snd_pcm_status64 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 ) ) ;
2018-04-24 15:06:11 +03:00
err = snd_pcm_status64 ( substream , & status ) ;
2005-04-17 02:20:36 +04:00
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 ) ) ;
2018-04-24 15:06:11 +03:00
snd_iprintf ( buffer , " trigger_time: %lld.%09lld \n " ,
status . trigger_tstamp_sec , status . trigger_tstamp_nsec ) ;
snd_iprintf ( buffer , " tstamp : %lld.%09lld \n " ,
status . tstamp_sec , status . tstamp_nsec ) ;
2005-04-17 02:20:36 +04:00
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 ;
2018-07-04 16:08:05 +03:00
snd_pcm_stop_xrun ( substream ) ;
2014-11-04 16:02:40 +03:00
}
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
pstr - > proc_root = entry ;
2017-08-23 10:20:29 +03:00
entry = snd_info_create_card_entry ( pcm - > card , " info " , pstr - > proc_root ) ;
2019-02-04 17:02:11 +03:00
if ( entry )
2006-04-28 17:13:41 +04:00
snd_info_set_text_ops ( entry , pstr , snd_pcm_stream_proc_info_read ) ;
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 ) {
2019-02-04 17:02:11 +03:00
snd_info_set_text_ops ( entry , pstr , snd_pcm_xrun_debug_read ) ;
2005-04-17 02:20:36 +04:00
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
}
# 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-06-23 16:37:59 +04:00
snd_info_free_entry ( pstr - > proc_root ) ;
pstr - > proc_root = NULL ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2019-02-04 17:02:11 +03:00
static struct snd_info_entry *
create_substream_info_entry ( struct snd_pcm_substream * substream ,
const char * name ,
void ( * read ) ( struct snd_info_entry * ,
struct snd_info_buffer * ) )
{
struct snd_info_entry * entry ;
entry = snd_info_create_card_entry ( substream - > pcm - > card , name ,
substream - > proc_root ) ;
if ( entry )
snd_info_set_text_ops ( entry , substream , read ) ;
return entry ;
}
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
substream - > proc_root = entry ;
2019-02-04 17:02:11 +03:00
create_substream_info_entry ( substream , " info " ,
snd_pcm_substream_proc_info_read ) ;
create_substream_info_entry ( substream , " hw_params " ,
snd_pcm_substream_proc_hw_params_read ) ;
create_substream_info_entry ( substream , " sw_params " ,
snd_pcm_substream_proc_sw_params_read ) ;
create_substream_info_entry ( substream , " status " ,
snd_pcm_substream_proc_status_read ) ;
2005-04-17 02:20:36 +04:00
2014-11-04 16:02:40 +03:00
# ifdef CONFIG_SND_PCM_XRUN_DEBUG
2019-02-04 17:02:11 +03:00
entry = create_substream_info_entry ( substream , " xrun_injection " , NULL ) ;
2014-11-04 16:02:40 +03:00
if ( entry ) {
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
}
# endif /* CONFIG_SND_PCM_XRUN_DEBUG */
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-06-23 16:37:59 +04:00
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 ; }
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 [ ] ;
2019-01-11 17:58:39 +03:00
/*
* PM callbacks : we need to deal only with suspend here , as the resume is
* triggered either from user - space or the driver ' s resume callback
*/
# ifdef CONFIG_PM_SLEEP
static int do_pcm_suspend ( struct device * dev )
{
struct snd_pcm_str * pstr = container_of ( dev , struct snd_pcm_str , dev ) ;
if ( ! pstr - > pcm - > no_device_suspend )
snd_pcm_suspend_all ( pstr - > pcm ) ;
return 0 ;
}
# endif
static const struct dev_pm_ops pcm_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( do_pcm_suspend , NULL )
} ;
/* device type for PCM -- basically only for passing PM callbacks */
static const struct device_type pcm_dev_type = {
. name = " pcm " ,
. pm = & pcm_dev_pm_ops ,
} ;
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 ;
2019-01-11 17:58:39 +03:00
pstr - > dev . type = & pcm_dev_type ;
2015-01-29 19:32:26 +03:00
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 ;
2019-01-13 11:35:17 +03:00
snd_pcm_group_init ( & substream - > self_group ) ;
2005-04-17 02:20:36 +04:00
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 ;
2020-01-03 11:16:20 +03:00
static const 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 ,
} ;
2020-01-03 11:16:20 +03:00
static const struct snd_device_ops internal_ops = {
2017-10-17 12:40:55 +03:00
. 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 )
ALSA: Convert strlcpy to strscpy when return value is unused
strlcpy is deprecated. see: Documentation/process/deprecated.rst
Change the calls that do not use the strlcpy return value to the
preferred strscpy.
Done with cocci script:
@@
expression e1, e2, e3;
@@
- strlcpy(
+ strscpy(
e1, e2, e3);
This cocci script leaves the instances where the return value is
used unchanged.
After this patch, sound/ has 3 uses of strlcpy() that need to be
manually inspected for conversion and changed one day.
$ git grep -w strlcpy sound/
sound/usb/card.c: len = strlcpy(card->longname, s, sizeof(card->longname));
sound/usb/mixer.c: return strlcpy(buf, p->name, buflen);
sound/usb/mixer.c: return strlcpy(buf, p->names[index], buflen);
Miscellenea:
o Remove trailing whitespace in conversion of sound/core/hwdep.c
Link: https://lore.kernel.org/lkml/CAHk-=wgfRnXz0W3D37d01q3JFkr_i_uTL=V6A6G1oUZcprmknw@mail.gmail.com/
Signed-off-by: Joe Perches <joe@perches.com>
Acked-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/22b393d1790bb268769d0bab7bacf0866dcb0c14.camel@perches.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-01-04 20:17:34 +03:00
strscpy ( 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 ) {
2021-11-16 10:13:13 +03:00
struct snd_card * card = pstr - > pcm - > card ;
down_write ( & card - > controls_rwsem ) ;
snd_ctl_remove ( card , pstr - > chmap_kctl ) ;
up_write ( & card - > controls_rwsem ) ;
2016-07-08 09:23:43 +03:00
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
2019-01-23 18:44:38 +03:00
/* free all proc files under the stream */
snd_pcm_stream_proc_done ( pstr ) ;
2005-04-17 02:20:36 +04:00
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
kfree ( substream ) ;
substream = substream_next ;
}
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 ) ) ;
2018-11-23 21:38:13 +03:00
runtime - > status = alloc_pages_exact ( size , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( runtime - > status = = NULL ) {
kfree ( runtime ) ;
return - ENOMEM ;
}
2018-11-23 21:38:13 +03:00
memset ( runtime - > status , 0 , size ) ;
2005-04-17 02:20:36 +04:00
2005-11-17 15:59:38 +03:00
size = PAGE_ALIGN ( sizeof ( struct snd_pcm_mmap_control ) ) ;
2018-11-23 21:38:13 +03:00
runtime - > control = alloc_pages_exact ( size , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( runtime - > control = = NULL ) {
2018-11-23 21:38:13 +03:00
free_pages_exact ( runtime - > status ,
2005-11-17 15:59:38 +03:00
PAGE_ALIGN ( sizeof ( struct snd_pcm_mmap_status ) ) ) ;
2005-04-17 02:20:36 +04:00
kfree ( runtime ) ;
return - ENOMEM ;
}
2018-11-23 21:38:13 +03:00
memset ( runtime - > control , 0 , size ) ;
2005-04-17 02:20:36 +04:00
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
2022-09-26 16:55:48 +03:00
__snd_pcm_set_state ( runtime , SNDRV_PCM_STATE_OPEN ) ;
2022-03-22 20:07:17 +03:00
mutex_init ( & runtime - > buffer_mutex ) ;
ALSA: pcm: Fix potential AB/BA lock with buffer_mutex and mmap_lock
syzbot caught a potential deadlock between the PCM
runtime->buffer_mutex and the mm->mmap_lock. It was brought by the
recent fix to cover the racy read/write and other ioctls, and in that
commit, I overlooked a (hopefully only) corner case that may take the
revert lock, namely, the OSS mmap. The OSS mmap operation
exceptionally allows to re-configure the parameters inside the OSS
mmap syscall, where mm->mmap_mutex is already held. Meanwhile, the
copy_from/to_user calls at read/write operations also take the
mm->mmap_lock internally, hence it may lead to a AB/BA deadlock.
A similar problem was already seen in the past and we fixed it with a
refcount (in commit b248371628aa). The former fix covered only the
call paths with OSS read/write and OSS ioctls, while we need to cover
the concurrent access via both ALSA and OSS APIs now.
This patch addresses the problem above by replacing the buffer_mutex
lock in the read/write operations with a refcount similar as we've
used for OSS. The new field, runtime->buffer_accessing, keeps the
number of concurrent read/write operations. Unlike the former
buffer_mutex protection, this protects only around the
copy_from/to_user() calls; the other codes are basically protected by
the PCM stream lock. The refcount can be a negative, meaning blocked
by the ioctls. If a negative value is seen, the read/write aborts
with -EBUSY. In the ioctl side, OTOH, they check this refcount, too,
and set to a negative value for blocking unless it's already being
accessed.
Reported-by: syzbot+6e5c88838328e99c7e1c@syzkaller.appspotmail.com
Fixes: dca947d4d26d ("ALSA: pcm: Fix races among concurrent read/write and buffer changes")
Cc: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/000000000000381a0d05db622a81@google.com
Link: https://lore.kernel.org/r/20220330120903.4738-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2022-03-30 15:09:03 +03:00
atomic_set ( & runtime - > buffer_accessing , 0 ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
2018-11-23 21:38:13 +03:00
free_pages_exact ( runtime - > status ,
2005-11-17 15:59:38 +03:00
PAGE_ALIGN ( sizeof ( struct snd_pcm_mmap_status ) ) ) ;
2018-11-23 21:38:13 +03:00
free_pages_exact ( runtime - > control ,
2005-11-17 15:59:38 +03:00
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 */
2020-09-03 00:21:16 +03:00
if ( substream - > timer ) {
2018-04-02 23:41:43 +03:00
spin_lock_irq ( & substream - > timer - > lock ) ;
2020-09-03 00:21:16 +03:00
substream - > runtime = NULL ;
2018-04-02 23:41:43 +03:00
spin_unlock_irq ( & substream - > timer - > lock ) ;
2020-09-03 00:21:16 +03:00
} else {
substream - > runtime = NULL ;
}
2022-03-22 20:07:17 +03:00
mutex_destroy ( & runtime - > buffer_mutex ) ;
2022-07-28 15:59:44 +03:00
snd_fasync_free ( runtime - > fasync ) ;
2018-04-02 23:41:43 +03:00
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 - - ;
}
2021-05-24 15:00:07 +03:00
static ssize_t pcm_class_show ( struct device * dev ,
2006-08-08 09:19:37 +04:00
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 ] ;
2022-08-01 19:56:37 +03:00
return sysfs_emit ( buf , " %s \n " , str ) ;
2006-09-06 16:27:46 +04:00
}
2021-05-24 15:00:07 +03:00
static DEVICE_ATTR_RO ( pcm_class ) ;
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 ) ;
2021-02-06 23:36:56 +03:00
for_each_pcm_substream ( pcm , cidx , substream ) {
snd_pcm_stream_lock_irq ( substream ) ;
if ( substream - > runtime ) {
if ( snd_pcm_running ( substream ) )
snd_pcm_stop ( substream , SNDRV_PCM_STATE_DISCONNECTED ) ;
/* to be sure, set the state unconditionally */
2022-09-26 16:55:48 +03:00
__snd_pcm_set_state ( substream - > runtime ,
SNDRV_PCM_STATE_DISCONNECTED ) ;
2021-02-06 23:36:56 +03:00
wake_up ( & substream - > runtime - > sleep ) ;
wake_up ( & substream - > runtime - > tsleep ) ;
2012-10-12 17:07:34 +04:00
}
2021-02-06 23:36:56 +03:00
snd_pcm_stream_unlock_irq ( substream ) ;
2015-02-20 19:04:08 +03:00
}
2017-10-17 12:40:55 +03:00
2021-02-06 23:36:56 +03:00
for_each_pcm_substream ( pcm , cidx , substream )
snd_pcm_sync_stop ( substream , false ) ;
2021-02-06 23:36:53 +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 .
2022-07-13 13:47:54 +03:00
*
* Return : zero if successful , or a negative error code
2014-10-30 17:02:50 +03: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 ) ;
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 )