2018-05-14 09:27:38 +03:00
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* Xen para - virtual sound device
*
* Copyright ( C ) 2016 - 2018 EPAM Systems Inc .
*
* Author : Oleksandr Andrushchenko < oleksandr_andrushchenko @ epam . com >
*/
# include <xen/xenbus.h>
# include <xen/interface/io/sndif.h>
# include "xen_snd_front.h"
# include "xen_snd_front_cfg.h"
/* Maximum number of supported streams. */
# define VSND_MAX_STREAM 8
struct cfg_hw_sample_rate {
const char * name ;
unsigned int mask ;
unsigned int value ;
} ;
static const struct cfg_hw_sample_rate CFG_HW_SUPPORTED_RATES [ ] = {
{ . name = " 5512 " , . mask = SNDRV_PCM_RATE_5512 , . value = 5512 } ,
{ . name = " 8000 " , . mask = SNDRV_PCM_RATE_8000 , . value = 8000 } ,
{ . name = " 11025 " , . mask = SNDRV_PCM_RATE_11025 , . value = 11025 } ,
{ . name = " 16000 " , . mask = SNDRV_PCM_RATE_16000 , . value = 16000 } ,
{ . name = " 22050 " , . mask = SNDRV_PCM_RATE_22050 , . value = 22050 } ,
{ . name = " 32000 " , . mask = SNDRV_PCM_RATE_32000 , . value = 32000 } ,
{ . name = " 44100 " , . mask = SNDRV_PCM_RATE_44100 , . value = 44100 } ,
{ . name = " 48000 " , . mask = SNDRV_PCM_RATE_48000 , . value = 48000 } ,
{ . name = " 64000 " , . mask = SNDRV_PCM_RATE_64000 , . value = 64000 } ,
{ . name = " 96000 " , . mask = SNDRV_PCM_RATE_96000 , . value = 96000 } ,
{ . name = " 176400 " , . mask = SNDRV_PCM_RATE_176400 , . value = 176400 } ,
{ . name = " 192000 " , . mask = SNDRV_PCM_RATE_192000 , . value = 192000 } ,
} ;
struct cfg_hw_sample_format {
const char * name ;
u64 mask ;
} ;
static const struct cfg_hw_sample_format CFG_HW_SUPPORTED_FORMATS [ ] = {
{
. name = XENSND_PCM_FORMAT_U8_STR ,
. mask = SNDRV_PCM_FMTBIT_U8
} ,
{
. name = XENSND_PCM_FORMAT_S8_STR ,
. mask = SNDRV_PCM_FMTBIT_S8
} ,
{
. name = XENSND_PCM_FORMAT_U16_LE_STR ,
. mask = SNDRV_PCM_FMTBIT_U16_LE
} ,
{
. name = XENSND_PCM_FORMAT_U16_BE_STR ,
. mask = SNDRV_PCM_FMTBIT_U16_BE
} ,
{
. name = XENSND_PCM_FORMAT_S16_LE_STR ,
. mask = SNDRV_PCM_FMTBIT_S16_LE
} ,
{
. name = XENSND_PCM_FORMAT_S16_BE_STR ,
. mask = SNDRV_PCM_FMTBIT_S16_BE
} ,
{
. name = XENSND_PCM_FORMAT_U24_LE_STR ,
. mask = SNDRV_PCM_FMTBIT_U24_LE
} ,
{
. name = XENSND_PCM_FORMAT_U24_BE_STR ,
. mask = SNDRV_PCM_FMTBIT_U24_BE
} ,
{
. name = XENSND_PCM_FORMAT_S24_LE_STR ,
. mask = SNDRV_PCM_FMTBIT_S24_LE
} ,
{
. name = XENSND_PCM_FORMAT_S24_BE_STR ,
. mask = SNDRV_PCM_FMTBIT_S24_BE
} ,
{
. name = XENSND_PCM_FORMAT_U32_LE_STR ,
. mask = SNDRV_PCM_FMTBIT_U32_LE
} ,
{
. name = XENSND_PCM_FORMAT_U32_BE_STR ,
. mask = SNDRV_PCM_FMTBIT_U32_BE
} ,
{
. name = XENSND_PCM_FORMAT_S32_LE_STR ,
. mask = SNDRV_PCM_FMTBIT_S32_LE
} ,
{
. name = XENSND_PCM_FORMAT_S32_BE_STR ,
. mask = SNDRV_PCM_FMTBIT_S32_BE
} ,
{
. name = XENSND_PCM_FORMAT_A_LAW_STR ,
. mask = SNDRV_PCM_FMTBIT_A_LAW
} ,
{
. name = XENSND_PCM_FORMAT_MU_LAW_STR ,
. mask = SNDRV_PCM_FMTBIT_MU_LAW
} ,
{
. name = XENSND_PCM_FORMAT_F32_LE_STR ,
. mask = SNDRV_PCM_FMTBIT_FLOAT_LE
} ,
{
. name = XENSND_PCM_FORMAT_F32_BE_STR ,
. mask = SNDRV_PCM_FMTBIT_FLOAT_BE
} ,
{
. name = XENSND_PCM_FORMAT_F64_LE_STR ,
. mask = SNDRV_PCM_FMTBIT_FLOAT64_LE
} ,
{
. name = XENSND_PCM_FORMAT_F64_BE_STR ,
. mask = SNDRV_PCM_FMTBIT_FLOAT64_BE
} ,
{
. name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE_STR ,
. mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
} ,
{
. name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE_STR ,
. mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE
} ,
{
. name = XENSND_PCM_FORMAT_IMA_ADPCM_STR ,
. mask = SNDRV_PCM_FMTBIT_IMA_ADPCM
} ,
{
. name = XENSND_PCM_FORMAT_MPEG_STR ,
. mask = SNDRV_PCM_FMTBIT_MPEG
} ,
{
. name = XENSND_PCM_FORMAT_GSM_STR ,
. mask = SNDRV_PCM_FMTBIT_GSM
} ,
} ;
static void cfg_hw_rates ( char * list , unsigned int len ,
const char * path , struct snd_pcm_hardware * pcm_hw )
{
char * cur_rate ;
unsigned int cur_mask ;
unsigned int cur_value ;
unsigned int rates ;
unsigned int rate_min ;
unsigned int rate_max ;
int i ;
rates = 0 ;
rate_min = - 1 ;
rate_max = 0 ;
while ( ( cur_rate = strsep ( & list , XENSND_LIST_SEPARATOR ) ) ) {
for ( i = 0 ; i < ARRAY_SIZE ( CFG_HW_SUPPORTED_RATES ) ; i + + )
if ( ! strncasecmp ( cur_rate ,
CFG_HW_SUPPORTED_RATES [ i ] . name ,
XENSND_SAMPLE_RATE_MAX_LEN ) ) {
cur_mask = CFG_HW_SUPPORTED_RATES [ i ] . mask ;
cur_value = CFG_HW_SUPPORTED_RATES [ i ] . value ;
rates | = cur_mask ;
if ( rate_min > cur_value )
rate_min = cur_value ;
if ( rate_max < cur_value )
rate_max = cur_value ;
}
}
if ( rates ) {
pcm_hw - > rates = rates ;
pcm_hw - > rate_min = rate_min ;
pcm_hw - > rate_max = rate_max ;
}
}
static void cfg_formats ( char * list , unsigned int len ,
const char * path , struct snd_pcm_hardware * pcm_hw )
{
u64 formats ;
char * cur_format ;
int i ;
formats = 0 ;
while ( ( cur_format = strsep ( & list , XENSND_LIST_SEPARATOR ) ) ) {
for ( i = 0 ; i < ARRAY_SIZE ( CFG_HW_SUPPORTED_FORMATS ) ; i + + )
if ( ! strncasecmp ( cur_format ,
CFG_HW_SUPPORTED_FORMATS [ i ] . name ,
XENSND_SAMPLE_FORMAT_MAX_LEN ) )
formats | = CFG_HW_SUPPORTED_FORMATS [ i ] . mask ;
}
if ( formats )
pcm_hw - > formats = formats ;
}
# define MAX_BUFFER_SIZE (64 * 1024)
# define MIN_PERIOD_SIZE 64
# define MAX_PERIOD_SIZE MAX_BUFFER_SIZE
# define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | \
SNDRV_PCM_FMTBIT_S16_LE )
# define USE_RATE (SNDRV_PCM_RATE_CONTINUOUS | \
SNDRV_PCM_RATE_8000_48000 )
# define USE_RATE_MIN 5512
# define USE_RATE_MAX 48000
# define USE_CHANNELS_MIN 1
# define USE_CHANNELS_MAX 2
# define USE_PERIODS_MIN 2
# define USE_PERIODS_MAX (MAX_BUFFER_SIZE / MIN_PERIOD_SIZE)
static const struct snd_pcm_hardware SND_DRV_PCM_HW_DEFAULT = {
. info = ( SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID ) ,
. formats = USE_FORMATS ,
. rates = USE_RATE ,
. rate_min = USE_RATE_MIN ,
. rate_max = USE_RATE_MAX ,
. channels_min = USE_CHANNELS_MIN ,
. channels_max = USE_CHANNELS_MAX ,
. buffer_bytes_max = MAX_BUFFER_SIZE ,
. period_bytes_min = MIN_PERIOD_SIZE ,
. period_bytes_max = MAX_PERIOD_SIZE ,
. periods_min = USE_PERIODS_MIN ,
. periods_max = USE_PERIODS_MAX ,
. fifo_size = 0 ,
} ;
static void cfg_read_pcm_hw ( const char * path ,
struct snd_pcm_hardware * parent_pcm_hw ,
struct snd_pcm_hardware * pcm_hw )
{
char * list ;
int val ;
size_t buf_sz ;
unsigned int len ;
/* Inherit parent's PCM HW and read overrides from XenStore. */
if ( parent_pcm_hw )
* pcm_hw = * parent_pcm_hw ;
else
* pcm_hw = SND_DRV_PCM_HW_DEFAULT ;
val = xenbus_read_unsigned ( path , XENSND_FIELD_CHANNELS_MIN , 0 ) ;
if ( val )
pcm_hw - > channels_min = val ;
val = xenbus_read_unsigned ( path , XENSND_FIELD_CHANNELS_MAX , 0 ) ;
if ( val )
pcm_hw - > channels_max = val ;
list = xenbus_read ( XBT_NIL , path , XENSND_FIELD_SAMPLE_RATES , & len ) ;
if ( ! IS_ERR ( list ) ) {
cfg_hw_rates ( list , len , path , pcm_hw ) ;
kfree ( list ) ;
}
list = xenbus_read ( XBT_NIL , path , XENSND_FIELD_SAMPLE_FORMATS , & len ) ;
if ( ! IS_ERR ( list ) ) {
cfg_formats ( list , len , path , pcm_hw ) ;
kfree ( list ) ;
}
buf_sz = xenbus_read_unsigned ( path , XENSND_FIELD_BUFFER_SIZE , 0 ) ;
if ( buf_sz )
pcm_hw - > buffer_bytes_max = buf_sz ;
/* Update configuration to match new values. */
if ( pcm_hw - > channels_min > pcm_hw - > channels_max )
pcm_hw - > channels_min = pcm_hw - > channels_max ;
if ( pcm_hw - > rate_min > pcm_hw - > rate_max )
pcm_hw - > rate_min = pcm_hw - > rate_max ;
pcm_hw - > period_bytes_max = pcm_hw - > buffer_bytes_max ;
pcm_hw - > periods_max = pcm_hw - > period_bytes_max /
pcm_hw - > period_bytes_min ;
}
static int cfg_get_stream_type ( const char * path , int index ,
int * num_pb , int * num_cap )
{
char * str = NULL ;
char * stream_path ;
int ret ;
* num_pb = 0 ;
* num_cap = 0 ;
stream_path = kasprintf ( GFP_KERNEL , " %s/%d " , path , index ) ;
if ( ! stream_path ) {
ret = - ENOMEM ;
goto fail ;
}
str = xenbus_read ( XBT_NIL , stream_path , XENSND_FIELD_TYPE , NULL ) ;
if ( IS_ERR ( str ) ) {
ret = PTR_ERR ( str ) ;
2018-05-30 13:36:55 +03:00
str = NULL ;
2018-05-14 09:27:38 +03:00
goto fail ;
}
if ( ! strncasecmp ( str , XENSND_STREAM_TYPE_PLAYBACK ,
sizeof ( XENSND_STREAM_TYPE_PLAYBACK ) ) ) {
( * num_pb ) + + ;
} else if ( ! strncasecmp ( str , XENSND_STREAM_TYPE_CAPTURE ,
sizeof ( XENSND_STREAM_TYPE_CAPTURE ) ) ) {
( * num_cap ) + + ;
} else {
ret = - EINVAL ;
goto fail ;
}
ret = 0 ;
fail :
kfree ( stream_path ) ;
kfree ( str ) ;
return ret ;
}
static int cfg_stream ( struct xen_snd_front_info * front_info ,
struct xen_front_cfg_pcm_instance * pcm_instance ,
const char * path , int index , int * cur_pb , int * cur_cap ,
int * stream_cnt )
{
char * str = NULL ;
char * stream_path ;
struct xen_front_cfg_stream * stream ;
int ret ;
stream_path = devm_kasprintf ( & front_info - > xb_dev - > dev ,
GFP_KERNEL , " %s/%d " , path , index ) ;
if ( ! stream_path ) {
ret = - ENOMEM ;
goto fail ;
}
str = xenbus_read ( XBT_NIL , stream_path , XENSND_FIELD_TYPE , NULL ) ;
if ( IS_ERR ( str ) ) {
ret = PTR_ERR ( str ) ;
2018-05-30 13:36:55 +03:00
str = NULL ;
2018-05-14 09:27:38 +03:00
goto fail ;
}
if ( ! strncasecmp ( str , XENSND_STREAM_TYPE_PLAYBACK ,
sizeof ( XENSND_STREAM_TYPE_PLAYBACK ) ) ) {
stream = & pcm_instance - > streams_pb [ ( * cur_pb ) + + ] ;
} else if ( ! strncasecmp ( str , XENSND_STREAM_TYPE_CAPTURE ,
sizeof ( XENSND_STREAM_TYPE_CAPTURE ) ) ) {
stream = & pcm_instance - > streams_cap [ ( * cur_cap ) + + ] ;
} else {
ret = - EINVAL ;
goto fail ;
}
/* Get next stream index. */
stream - > index = ( * stream_cnt ) + + ;
stream - > xenstore_path = stream_path ;
/*
* Check XenStore if PCM HW configuration exists for this stream
* and update if so , e . g . we inherit all values from device ' s PCM HW ,
* but can still override some of the values for the stream .
*/
cfg_read_pcm_hw ( stream - > xenstore_path ,
& pcm_instance - > pcm_hw , & stream - > pcm_hw ) ;
ret = 0 ;
fail :
kfree ( str ) ;
return ret ;
}
static int cfg_device ( struct xen_snd_front_info * front_info ,
struct xen_front_cfg_pcm_instance * pcm_instance ,
struct snd_pcm_hardware * parent_pcm_hw ,
const char * path , int node_index , int * stream_cnt )
{
char * str ;
char * device_path ;
int ret , i , num_streams ;
int num_pb , num_cap ;
int cur_pb , cur_cap ;
char node [ 3 ] ;
device_path = kasprintf ( GFP_KERNEL , " %s/%d " , path , node_index ) ;
if ( ! device_path )
return - ENOMEM ;
str = xenbus_read ( XBT_NIL , device_path , XENSND_FIELD_DEVICE_NAME , NULL ) ;
if ( ! IS_ERR ( str ) ) {
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_instance - > name , str , sizeof ( pcm_instance - > name ) ) ;
2018-05-14 09:27:38 +03:00
kfree ( str ) ;
}
pcm_instance - > device_id = node_index ;
/*
* Check XenStore if PCM HW configuration exists for this device
* and update if so , e . g . we inherit all values from card ' s PCM HW ,
* but can still override some of the values for the device .
*/
cfg_read_pcm_hw ( device_path , parent_pcm_hw , & pcm_instance - > pcm_hw ) ;
/* Find out how many streams were configured in Xen store. */
num_streams = 0 ;
do {
snprintf ( node , sizeof ( node ) , " %d " , num_streams ) ;
if ( ! xenbus_exists ( XBT_NIL , device_path , node ) )
break ;
num_streams + + ;
} while ( num_streams < VSND_MAX_STREAM ) ;
pcm_instance - > num_streams_pb = 0 ;
pcm_instance - > num_streams_cap = 0 ;
/* Get number of playback and capture streams. */
for ( i = 0 ; i < num_streams ; i + + ) {
ret = cfg_get_stream_type ( device_path , i , & num_pb , & num_cap ) ;
if ( ret < 0 )
goto fail ;
pcm_instance - > num_streams_pb + = num_pb ;
pcm_instance - > num_streams_cap + = num_cap ;
}
if ( pcm_instance - > num_streams_pb ) {
pcm_instance - > streams_pb =
devm_kcalloc ( & front_info - > xb_dev - > dev ,
pcm_instance - > num_streams_pb ,
sizeof ( struct xen_front_cfg_stream ) ,
GFP_KERNEL ) ;
if ( ! pcm_instance - > streams_pb ) {
ret = - ENOMEM ;
goto fail ;
}
}
if ( pcm_instance - > num_streams_cap ) {
pcm_instance - > streams_cap =
devm_kcalloc ( & front_info - > xb_dev - > dev ,
pcm_instance - > num_streams_cap ,
sizeof ( struct xen_front_cfg_stream ) ,
GFP_KERNEL ) ;
if ( ! pcm_instance - > streams_cap ) {
ret = - ENOMEM ;
goto fail ;
}
}
cur_pb = 0 ;
cur_cap = 0 ;
for ( i = 0 ; i < num_streams ; i + + ) {
ret = cfg_stream ( front_info , pcm_instance , device_path , i ,
& cur_pb , & cur_cap , stream_cnt ) ;
if ( ret < 0 )
goto fail ;
}
ret = 0 ;
fail :
kfree ( device_path ) ;
return ret ;
}
int xen_snd_front_cfg_card ( struct xen_snd_front_info * front_info ,
int * stream_cnt )
{
struct xenbus_device * xb_dev = front_info - > xb_dev ;
struct xen_front_cfg_card * cfg = & front_info - > cfg ;
int ret , num_devices , i ;
char node [ 3 ] ;
* stream_cnt = 0 ;
num_devices = 0 ;
do {
snprintf ( node , sizeof ( node ) , " %d " , num_devices ) ;
if ( ! xenbus_exists ( XBT_NIL , xb_dev - > nodename , node ) )
break ;
num_devices + + ;
} while ( num_devices < SNDRV_PCM_DEVICES ) ;
if ( ! num_devices ) {
dev_warn ( & xb_dev - > dev ,
" No devices configured for sound card at %s \n " ,
xb_dev - > nodename ) ;
return - ENODEV ;
}
/* Start from default PCM HW configuration for the card. */
cfg_read_pcm_hw ( xb_dev - > nodename , NULL , & cfg - > pcm_hw ) ;
cfg - > pcm_instances =
devm_kcalloc ( & front_info - > xb_dev - > dev , num_devices ,
sizeof ( struct xen_front_cfg_pcm_instance ) ,
GFP_KERNEL ) ;
if ( ! cfg - > pcm_instances )
return - ENOMEM ;
for ( i = 0 ; i < num_devices ; i + + ) {
ret = cfg_device ( front_info , & cfg - > pcm_instances [ i ] ,
& cfg - > pcm_hw , xb_dev - > nodename , i , stream_cnt ) ;
if ( ret < 0 )
return ret ;
}
cfg - > num_pcm_instances = num_devices ;
return 0 ;
}