2001-05-10 08:32:40 +00:00
/**
* \ file control / setup . c
2001-07-18 12:17:11 +00:00
* \ brief Routines to setup control primitives from configuration
2001-06-11 13:35:48 +00:00
* \ author Abramo Bagnara < abramo @ alsa - project . org >
2007-10-15 10:24:55 +02:00
* \ author Jaroslav Kysela < perex @ perex . cz >
2001-05-10 08:32:40 +00:00
* \ date 2001
*
* Routines to setup control primitives from configuration
*/
/*
* Control Interface - routines for setup from configuration
2001-06-11 13:35:48 +00:00
* Copyright ( c ) 2001 by Abramo Bagnara < abramo @ alsa - project . org >
2007-10-15 10:24:55 +02:00
* Jaroslav Kysela < perex @ perex . cz >
2001-05-10 08:32:40 +00:00
*
*
* This library is free software ; you can redistribute it and / or modify
2001-12-30 09:22:54 +00:00
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation ; either version 2.1 of
2001-05-10 08:32:40 +00:00
* 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
2001-12-30 09:22:54 +00:00
* GNU Lesser General Public License for more details .
2001-05-10 08:32:40 +00:00
*
2001-12-30 09:22:54 +00:00
* You should have received a copy of the GNU Lesser General Public
2001-05-10 08:32:40 +00:00
* License along with this library ; if not , write to the Free Software
2017-11-14 14:29:26 +01:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2001-05-10 08:32:40 +00:00
*
*/
2023-09-13 12:27:21 +02:00
# include "local.h"
2001-05-10 08:32:40 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <stdarg.h>
# include <unistd.h>
# include <string.h>
# include <ctype.h>
2005-05-24 14:14:28 +00:00
# ifndef DOC_HIDDEN
2001-06-11 13:35:48 +00:00
typedef struct {
unsigned int lock : 1 ;
unsigned int preserve : 1 ;
snd_ctl_elem_id_t * id ;
snd_ctl_elem_info_t * info ;
snd_ctl_elem_value_t * val ;
snd_ctl_elem_value_t * mask ;
snd_ctl_elem_value_t * old ;
2001-05-10 08:32:40 +00:00
struct list_head list ;
} snd_sctl_elem_t ;
struct _snd_sctl {
2001-06-11 13:35:48 +00:00
int mode ;
snd_ctl_t * ctl ;
struct list_head elems ;
2001-05-10 08:32:40 +00:00
} ;
2005-05-24 14:14:28 +00:00
# endif /* DOC_HIDDEN */
2001-05-10 08:32:40 +00:00
2001-06-11 13:35:48 +00:00
static int free_elems ( snd_sctl_t * h )
2001-05-10 08:32:40 +00:00
{
2001-06-11 13:35:48 +00:00
int err = 0 ;
while ( ! list_empty ( & h - > elems ) ) {
snd_sctl_elem_t * elem = list_entry ( h - > elems . next , snd_sctl_elem_t , list ) ;
snd_ctl_elem_id_free ( elem - > id ) ;
snd_ctl_elem_info_free ( elem - > info ) ;
snd_ctl_elem_value_free ( elem - > val ) ;
snd_ctl_elem_value_free ( elem - > mask ) ;
snd_ctl_elem_value_free ( elem - > old ) ;
list_del ( & elem - > list ) ;
free ( elem ) ;
}
if ( ( h - > mode & SND_SCTL_NOFREE ) = = 0 )
err = snd_ctl_close ( h - > ctl ) ;
free ( h ) ;
return err ;
2001-05-10 08:32:40 +00:00
}
2001-07-11 15:48:27 +00:00
/**
* \ brief Install given values to control elements
* \ param h Setup control handle
* \ result zero if success , otherwise a negative error code
*/
2001-06-11 13:35:48 +00:00
int snd_sctl_install ( snd_sctl_t * h )
2001-05-10 08:32:40 +00:00
{
2001-06-11 13:35:48 +00:00
struct list_head * pos ;
int err ;
unsigned int k ;
assert ( h ) ;
list_for_each ( pos , & h - > elems ) {
snd_sctl_elem_t * elem = list_entry ( pos , snd_sctl_elem_t , list ) ;
unsigned int count ;
snd_ctl_elem_type_t type ;
if ( elem - > lock ) {
err = snd_ctl_elem_lock ( h - > ctl , elem - > id ) ;
if ( err < 0 ) {
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " Cannot lock ctl elem " ) ;
2001-06-11 13:35:48 +00:00
return err ;
}
}
err = snd_ctl_elem_read ( h - > ctl , elem - > old ) ;
if ( err < 0 ) {
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " Cannot read ctl elem " ) ;
2001-06-11 13:35:48 +00:00
return err ;
}
count = snd_ctl_elem_info_get_count ( elem - > info ) ;
type = snd_ctl_elem_info_get_type ( elem - > info ) ;
switch ( type ) {
case SND_CTL_ELEM_TYPE_BOOLEAN :
for ( k = 0 ; k < count ; + + k ) {
int old , val , mask ;
old = snd_ctl_elem_value_get_boolean ( elem - > old , k ) ;
mask = snd_ctl_elem_value_get_boolean ( elem - > mask , k ) ;
old & = ~ mask ;
if ( old ) {
val = snd_ctl_elem_value_get_boolean ( elem - > val , k ) ;
val | = old ;
snd_ctl_elem_value_set_boolean ( elem - > val , k , val ) ;
}
2001-05-10 08:32:40 +00:00
}
2001-06-11 13:35:48 +00:00
break ;
case SND_CTL_ELEM_TYPE_INTEGER :
for ( k = 0 ; k < count ; + + k ) {
long old , val , mask ;
old = snd_ctl_elem_value_get_integer ( elem - > old , k ) ;
mask = snd_ctl_elem_value_get_integer ( elem - > mask , k ) ;
old & = ~ mask ;
if ( old ) {
val = snd_ctl_elem_value_get_integer ( elem - > val , k ) ;
val | = old ;
snd_ctl_elem_value_set_integer ( elem - > val , k , val ) ;
}
}
break ;
case SND_CTL_ELEM_TYPE_ENUMERATED :
for ( k = 0 ; k < count ; + + k ) {
unsigned int old , val , mask ;
old = snd_ctl_elem_value_get_enumerated ( elem - > old , k ) ;
mask = snd_ctl_elem_value_get_enumerated ( elem - > mask , k ) ;
old & = ~ mask ;
if ( old ) {
val = snd_ctl_elem_value_get_enumerated ( elem - > val , k ) ;
val | = old ;
snd_ctl_elem_value_set_enumerated ( elem - > val , k , val ) ;
}
}
break ;
case SND_CTL_ELEM_TYPE_IEC958 :
count = sizeof ( snd_aes_iec958_t ) ;
/* Fall through */
case SND_CTL_ELEM_TYPE_BYTES :
for ( k = 0 ; k < count ; + + k ) {
unsigned char old , val , mask ;
old = snd_ctl_elem_value_get_byte ( elem - > old , k ) ;
mask = snd_ctl_elem_value_get_byte ( elem - > mask , k ) ;
old & = ~ mask ;
if ( old ) {
val = snd_ctl_elem_value_get_byte ( elem - > val , k ) ;
val | = old ;
snd_ctl_elem_value_set_byte ( elem - > val , k , val ) ;
}
}
break ;
default :
assert ( 0 ) ;
break ;
}
err = snd_ctl_elem_write ( h - > ctl , elem - > val ) ;
if ( err < 0 ) {
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " Cannot write ctl elem " ) ;
2001-06-11 13:35:48 +00:00
return err ;
2001-05-10 08:32:40 +00:00
}
}
2001-06-11 13:35:48 +00:00
return 0 ;
2001-05-10 08:32:40 +00:00
}
2001-07-11 15:48:27 +00:00
/**
* \ brief Remove ( restore ) previous values from control elements
* \ param h Setup control handle
* \ result zero if success , otherwise a negative error code
*/
2001-06-11 13:35:48 +00:00
int snd_sctl_remove ( snd_sctl_t * h )
2001-05-10 08:32:40 +00:00
{
2001-06-11 13:35:48 +00:00
struct list_head * pos ;
int err ;
assert ( h ) ;
list_for_each ( pos , & h - > elems ) {
snd_sctl_elem_t * elem = list_entry ( pos , snd_sctl_elem_t , list ) ;
if ( elem - > lock ) {
err = snd_ctl_elem_unlock ( h - > ctl , elem - > id ) ;
if ( err < 0 ) {
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " Cannot unlock ctl elem " ) ;
2001-06-11 13:35:48 +00:00
return err ;
2001-05-10 08:32:40 +00:00
}
}
2009-12-25 14:22:38 -06:00
/* Only restore the old value if it differs from the requested
* value , because if it has changed restoring the old value
* overrides the change . Take for example , a voice modem with
* a . conf that sets preserve off - hook . Start playback ( on - hook
* to off - hook ) , start record ( off - hook to off - hook ) , stop
* playback ( off - hook to restore on - hook ) , stop record ( on - hook
* to restore off - hook ) , Clearly you don ' t want to leave the
* modem " on the phone " now that there isn ' t any playback or
* recording active .
*/
if ( elem - > preserve & & snd_ctl_elem_value_compare ( elem - > val , elem - > old ) ) {
2001-06-11 13:35:48 +00:00
err = snd_ctl_elem_write ( h - > ctl , elem - > old ) ;
if ( err < 0 ) {
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " Cannot restore ctl elem " ) ;
2001-06-11 13:35:48 +00:00
return err ;
2001-05-10 08:32:40 +00:00
}
}
2001-06-11 13:35:48 +00:00
}
return 0 ;
}
static int snd_config_get_ctl_elem_enumerated ( snd_config_t * n , snd_ctl_t * ctl ,
snd_ctl_elem_info_t * info )
{
const char * str ;
long val ;
unsigned int idx , items ;
2001-07-11 14:09:01 +00:00
switch ( snd_config_get_type ( n ) ) {
2001-06-11 13:35:48 +00:00
case SND_CONFIG_TYPE_INTEGER :
snd_config_get_integer ( n , & val ) ;
return val ;
case SND_CONFIG_TYPE_STRING :
snd_config_get_string ( n , & str ) ;
2001-05-10 08:32:40 +00:00
break ;
default :
2001-06-11 13:35:48 +00:00
return - 1 ;
}
items = snd_ctl_elem_info_get_items ( info ) ;
for ( idx = 0 ; idx < items ; idx + + ) {
int err ;
snd_ctl_elem_info_set_item ( info , idx ) ;
err = snd_ctl_elem_info ( ctl , info ) ;
if ( err < 0 ) {
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " Cannot obtain info for CTL elem " ) ;
2001-06-11 13:35:48 +00:00
return err ;
}
if ( strcmp ( str , snd_ctl_elem_info_get_item_name ( info ) ) = = 0 )
return idx ;
2001-05-10 08:32:40 +00:00
}
2001-06-11 13:35:48 +00:00
return - 1 ;
2001-05-10 08:32:40 +00:00
}
2001-06-11 13:35:48 +00:00
static int snd_config_get_ctl_elem_value ( snd_config_t * conf ,
snd_ctl_t * ctl ,
snd_ctl_elem_value_t * val ,
snd_ctl_elem_value_t * mask ,
snd_ctl_elem_info_t * info )
2001-05-10 08:32:40 +00:00
{
int err ;
2001-06-11 13:35:48 +00:00
snd_config_iterator_t i , next ;
2016-06-27 23:37:36 +09:00
snd_ctl_elem_id_t id = { 0 } ;
2001-06-11 13:35:48 +00:00
snd_ctl_elem_type_t type ;
unsigned int count ;
long v ;
long idx ;
2016-06-27 23:37:36 +09:00
snd_ctl_elem_value_get_id ( val , & id ) ;
2001-06-11 13:35:48 +00:00
count = snd_ctl_elem_info_get_count ( info ) ;
type = snd_ctl_elem_info_get_type ( info ) ;
if ( count = = 1 ) {
2001-07-11 14:09:01 +00:00
switch ( type ) {
2001-06-11 13:35:48 +00:00
case SND_CTL_ELEM_TYPE_BOOLEAN :
v = snd_config_get_bool ( conf ) ;
if ( v > = 0 ) {
snd_ctl_elem_value_set_boolean ( val , 0 , v ) ;
if ( mask )
snd_ctl_elem_value_set_boolean ( mask , 0 , 1 ) ;
return 0 ;
2001-05-10 08:32:40 +00:00
}
2001-06-11 13:35:48 +00:00
break ;
case SND_CTL_ELEM_TYPE_INTEGER :
err = snd_config_get_integer ( conf , & v ) ;
if ( err = = 0 ) {
snd_ctl_elem_value_set_integer ( val , 0 , v ) ;
if ( mask )
snd_ctl_elem_value_set_integer ( mask , 0 , ~ 0L ) ;
return 0 ;
2001-05-10 08:32:40 +00:00
}
2001-06-11 13:35:48 +00:00
break ;
case SND_CTL_ELEM_TYPE_ENUMERATED :
v = snd_config_get_ctl_elem_enumerated ( conf , ctl , info ) ;
if ( v > = 0 ) {
snd_ctl_elem_value_set_enumerated ( val , 0 , v ) ;
if ( mask )
snd_ctl_elem_value_set_enumerated ( mask , 0 , ~ 0 ) ;
return 0 ;
2001-05-10 08:32:40 +00:00
}
2001-06-11 13:35:48 +00:00
break ;
case SND_CTL_ELEM_TYPE_BYTES :
case SND_CTL_ELEM_TYPE_IEC958 :
break ;
default :
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " Unknown control type: %d " , type ) ;
2001-06-11 13:35:48 +00:00
return - EINVAL ;
}
}
2001-07-11 14:09:01 +00:00
switch ( type ) {
2001-06-11 13:35:48 +00:00
case SND_CTL_ELEM_TYPE_IEC958 :
count = sizeof ( snd_aes_iec958_t ) ;
/* Fall through */
case SND_CTL_ELEM_TYPE_BYTES :
{
const char * buf ;
err = snd_config_get_string ( conf , & buf ) ;
if ( err > = 0 ) {
int c1 = 0 ;
unsigned int len = strlen ( buf ) ;
unsigned int idx = 0 ;
if ( len % 2 ! = 0 | | len > count * 2 ) {
_bad_content :
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " bad value content " ) ;
2001-06-11 13:35:48 +00:00
return - EINVAL ;
2001-05-10 08:32:40 +00:00
}
2001-06-11 13:35:48 +00:00
while ( * buf ) {
int c = * buf + + ;
if ( c > = ' 0 ' & & c < = ' 9 ' )
c - = ' 0 ' ;
else if ( c > = ' a ' & & c < = ' f ' )
c = c - ' a ' + 10 ;
else if ( c > = ' A ' & & c < = ' F ' )
c = c - ' A ' + 10 ;
else {
goto _bad_content ;
}
if ( idx % 2 = = 1 ) {
snd_ctl_elem_value_set_byte ( val , idx / 2 , c1 < < 4 | c ) ;
if ( mask )
snd_ctl_elem_value_set_byte ( mask , idx / 2 , 0xff ) ;
} else
c1 = c ;
idx + + ;
2001-05-10 08:32:40 +00:00
}
2001-06-11 13:35:48 +00:00
return 0 ;
}
}
default :
break ;
}
if ( snd_config_get_type ( conf ) ! = SND_CONFIG_TYPE_COMPOUND ) {
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " bad value type " ) ;
2001-06-11 13:35:48 +00:00
return - EINVAL ;
}
snd_config_for_each ( i , next , conf ) {
snd_config_t * n = snd_config_iterator_entry ( i ) ;
2001-11-19 08:14:21 +00:00
const char * id ;
if ( snd_config_get_id ( n , & id ) < 0 )
continue ;
err = safe_strtol ( id , & idx ) ;
2001-06-11 13:35:48 +00:00
if ( err < 0 | | idx < 0 | | ( unsigned int ) idx > = count ) {
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " bad value index " ) ;
2001-06-11 13:35:48 +00:00
return - EINVAL ;
}
2001-07-11 14:09:01 +00:00
switch ( type ) {
2001-06-11 13:35:48 +00:00
case SND_CTL_ELEM_TYPE_BOOLEAN :
v = snd_config_get_bool ( n ) ;
if ( v < 0 )
goto _bad_content ;
snd_ctl_elem_value_set_boolean ( val , idx , v ) ;
if ( mask )
snd_ctl_elem_value_set_boolean ( mask , idx , 1 ) ;
break ;
case SND_CTL_ELEM_TYPE_INTEGER :
err = snd_config_get_integer ( n , & v ) ;
if ( err < 0 )
goto _bad_content ;
snd_ctl_elem_value_set_integer ( val , idx , v ) ;
if ( mask )
snd_ctl_elem_value_set_integer ( mask , idx , ~ 0L ) ;
break ;
case SND_CTL_ELEM_TYPE_ENUMERATED :
v = snd_config_get_ctl_elem_enumerated ( n , ctl , info ) ;
if ( v < 0 )
goto _bad_content ;
snd_ctl_elem_value_set_enumerated ( val , idx , v ) ;
if ( mask )
snd_ctl_elem_value_set_enumerated ( mask , idx , ~ 0 ) ;
break ;
case SND_CTL_ELEM_TYPE_BYTES :
case SND_CTL_ELEM_TYPE_IEC958 :
err = snd_config_get_integer ( n , & v ) ;
if ( err < 0 | | v < 0 | | v > 255 )
goto _bad_content ;
snd_ctl_elem_value_set_byte ( val , idx , v ) ;
if ( mask )
snd_ctl_elem_value_set_byte ( mask , idx , 0xff ) ;
break ;
default :
break ;
2001-05-10 08:32:40 +00:00
}
}
return 0 ;
}
2013-02-11 12:28:45 +01:00
static int add_elem ( snd_sctl_t * h , snd_config_t * _conf , snd_config_t * private_data , int * quit )
2001-06-11 13:35:48 +00:00
{
2001-06-18 14:14:49 +00:00
snd_config_t * conf ;
2001-06-11 13:35:48 +00:00
snd_config_iterator_t i , next ;
2001-06-15 08:47:59 +00:00
int iface = SND_CTL_ELEM_IFACE_MIXER ;
2001-06-18 14:14:49 +00:00
const char * name = NULL ;
2001-06-11 13:35:48 +00:00
long index = 0 ;
long device = - 1 ;
long subdevice = - 1 ;
int lock = 0 ;
int preserve = 0 ;
2003-02-25 16:57:21 +00:00
int optional = 0 ;
2013-02-11 12:28:45 +01:00
int skip_rest = 0 ;
2001-06-11 13:35:48 +00:00
snd_config_t * value = NULL , * mask = NULL ;
snd_sctl_elem_t * elem = NULL ;
int err ;
2001-06-18 14:14:49 +00:00
err = snd_config_expand ( _conf , _conf , NULL , private_data , & conf ) ;
if ( err < 0 )
return err ;
2001-06-11 13:35:48 +00:00
snd_config_for_each ( i , next , conf ) {
snd_config_t * n = snd_config_iterator_entry ( i ) ;
2001-11-19 08:14:21 +00:00
const char * id ;
if ( snd_config_get_id ( n , & id ) < 0 )
continue ;
2001-06-11 13:35:48 +00:00
if ( strcmp ( id , " comment " ) = = 0 )
continue ;
2001-06-15 08:47:59 +00:00
if ( strcmp ( id , " iface " ) = = 0 | | strcmp ( id , " interface " ) = = 0 ) {
2001-06-18 14:14:49 +00:00
const char * ptr ;
if ( ( err = snd_config_get_string ( n , & ptr ) ) < 0 ) {
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " field %s is not a string " , id ) ;
2001-06-11 13:35:48 +00:00
goto _err ;
2001-06-18 14:14:49 +00:00
}
if ( ( err = snd_config_get_ctl_iface_ascii ( ptr ) ) < 0 ) {
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " Invalid value for '%s' " , id ) ;
2001-06-11 13:35:48 +00:00
goto _err ;
}
2001-06-16 09:40:20 +00:00
iface = err ;
2001-06-11 13:35:48 +00:00
continue ;
2001-05-10 08:32:40 +00:00
}
2001-06-11 13:35:48 +00:00
if ( strcmp ( id , " name " ) = = 0 ) {
2001-06-18 14:14:49 +00:00
if ( ( err = snd_config_get_string ( n , & name ) ) < 0 ) {
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " field %s is not a string " , id ) ;
2001-06-11 13:35:48 +00:00
goto _err ;
2001-06-18 14:14:49 +00:00
}
2001-06-11 13:35:48 +00:00
continue ;
2001-05-10 08:32:40 +00:00
}
2001-06-11 13:35:48 +00:00
if ( strcmp ( id , " index " ) = = 0 ) {
2001-06-18 14:14:49 +00:00
if ( ( err = snd_config_get_integer ( n , & index ) ) < 0 ) {
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " field %s is not an integer " , id ) ;
2001-06-11 13:35:48 +00:00
goto _err ;
2001-06-18 14:14:49 +00:00
}
2001-06-15 08:47:59 +00:00
continue ;
2001-06-11 13:35:48 +00:00
}
if ( strcmp ( id , " device " ) = = 0 ) {
2001-06-18 14:14:49 +00:00
if ( ( err = snd_config_get_integer ( n , & device ) ) < 0 ) {
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " field %s is not an integer " , id ) ;
2001-06-11 13:35:48 +00:00
goto _err ;
2001-06-18 14:14:49 +00:00
}
2001-06-11 13:35:48 +00:00
continue ;
}
if ( strcmp ( id , " subdevice " ) = = 0 ) {
2001-06-18 14:14:49 +00:00
if ( ( err = snd_config_get_integer ( n , & subdevice ) ) < 0 ) {
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " field %s is not an integer " , id ) ;
2001-06-11 13:35:48 +00:00
goto _err ;
2001-06-18 14:14:49 +00:00
}
continue ;
2001-06-11 13:35:48 +00:00
}
if ( strcmp ( id , " lock " ) = = 0 ) {
2012-10-12 12:22:53 +02:00
err = snd_config_get_bool ( n ) ;
if ( err < 0 )
2001-06-11 13:35:48 +00:00
goto _err ;
2001-06-18 14:14:49 +00:00
lock = err ;
2001-06-11 13:35:48 +00:00
continue ;
}
if ( strcmp ( id , " preserve " ) = = 0 ) {
2012-10-12 12:22:53 +02:00
err = snd_config_get_bool ( n ) ;
if ( err < 0 )
2001-06-11 13:35:48 +00:00
goto _err ;
2001-06-18 14:14:49 +00:00
preserve = err ;
2001-06-11 13:35:48 +00:00
continue ;
}
if ( strcmp ( id , " value " ) = = 0 ) {
value = n ;
continue ;
}
if ( strcmp ( id , " mask " ) = = 0 ) {
mask = n ;
continue ;
}
2003-02-25 16:57:21 +00:00
if ( strcmp ( id , " optional " ) = = 0 ) {
2012-10-12 12:22:53 +02:00
err = snd_config_get_bool ( n ) ;
if ( err < 0 )
2003-02-25 16:57:21 +00:00
goto _err ;
optional = err ;
continue ;
}
2013-02-11 12:28:45 +01:00
if ( strcmp ( id , " skip_rest " ) = = 0 ) {
err = snd_config_get_bool ( n ) ;
if ( err < 0 )
goto _err ;
skip_rest = err ;
continue ;
}
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " Unknown field %s " , id ) ;
2001-06-11 13:35:48 +00:00
return - EINVAL ;
}
if ( name = = NULL ) {
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " Missing control name " ) ;
2001-06-11 13:35:48 +00:00
err = - EINVAL ;
goto _err ;
}
if ( value = = NULL ) {
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " Missing control value " ) ;
2001-06-11 13:35:48 +00:00
err = - EINVAL ;
goto _err ;
}
if ( device < 0 )
device = 0 ;
if ( subdevice < 0 )
subdevice = 0 ;
elem = calloc ( 1 , sizeof ( * elem ) ) ;
if ( ! elem )
return - ENOMEM ;
err = snd_ctl_elem_id_malloc ( & elem - > id ) ;
if ( err < 0 )
goto _err ;
err = snd_ctl_elem_info_malloc ( & elem - > info ) ;
if ( err < 0 )
goto _err ;
err = snd_ctl_elem_value_malloc ( & elem - > val ) ;
if ( err < 0 )
goto _err ;
err = snd_ctl_elem_value_malloc ( & elem - > mask ) ;
if ( err < 0 )
goto _err ;
err = snd_ctl_elem_value_malloc ( & elem - > old ) ;
if ( err < 0 )
goto _err ;
elem - > lock = lock ;
elem - > preserve = preserve ;
snd_ctl_elem_id_set_interface ( elem - > id , iface ) ;
snd_ctl_elem_id_set_name ( elem - > id , name ) ;
snd_ctl_elem_id_set_index ( elem - > id , index ) ;
snd_ctl_elem_id_set_device ( elem - > id , device ) ;
snd_ctl_elem_id_set_subdevice ( elem - > id , subdevice ) ;
snd_ctl_elem_info_set_id ( elem - > info , elem - > id ) ;
err = snd_ctl_elem_info ( h - > ctl , elem - > info ) ;
if ( err < 0 ) {
2003-02-25 16:57:21 +00:00
if ( ! optional )
2025-11-06 15:57:13 +01:00
snd_error ( CONTROL , " Cannot obtain info for CTL elem (%s,'%s',%li,%li,%li): %s " , snd_ctl_elem_iface_name ( iface ) , name , index , device , subdevice , snd_strerror ( err ) ) ;
2001-06-11 13:35:48 +00:00
goto _err ;
2013-02-11 12:28:45 +01:00
} else {
if ( skip_rest )
* quit = 1 ;
2001-06-11 13:35:48 +00:00
}
snd_ctl_elem_value_set_id ( elem - > val , elem - > id ) ;
snd_ctl_elem_value_set_id ( elem - > old , elem - > id ) ;
if ( mask ) {
err = snd_config_get_ctl_elem_value ( value , h - > ctl , elem - > val , NULL , elem - > info ) ;
if ( err < 0 )
goto _err ;
err = snd_config_get_ctl_elem_value ( mask , h - > ctl , elem - > mask , NULL , elem - > info ) ;
if ( err < 0 )
goto _err ;
} else {
err = snd_config_get_ctl_elem_value ( value , h - > ctl , elem - > val , elem - > mask , elem - > info ) ;
if ( err < 0 )
goto _err ;
}
err = snd_config_get_ctl_elem_value ( value , h - > ctl , elem - > val , elem - > mask , elem - > info ) ;
if ( err < 0 )
goto _err ;
list_add_tail ( & elem - > list , & h - > elems ) ;
_err :
2001-06-18 14:14:49 +00:00
if ( err < 0 & & elem ) {
2001-06-11 13:35:48 +00:00
if ( elem - > id )
snd_ctl_elem_id_free ( elem - > id ) ;
if ( elem - > info )
snd_ctl_elem_info_free ( elem - > info ) ;
if ( elem - > val )
snd_ctl_elem_value_free ( elem - > val ) ;
if ( elem - > mask )
snd_ctl_elem_value_free ( elem - > mask ) ;
if ( elem - > old )
snd_ctl_elem_value_free ( elem - > old ) ;
2001-05-10 08:32:40 +00:00
free ( elem ) ;
2003-02-25 16:57:21 +00:00
if ( err ! = - ENOMEM & & optional )
err = 0 ; /* ignore the error */
2001-05-10 08:32:40 +00:00
}
2001-06-18 14:14:49 +00:00
if ( conf )
snd_config_delete ( conf ) ;
2001-06-11 13:35:48 +00:00
return err ;
}
2001-07-11 15:48:27 +00:00
/**
* \ brief Build setup control handle
* \ param sctl Result - setup control handle
* \ param handle Master control handle
* \ param conf Setup configuration
* \ param private_data Private data for runtime evaluation
* \ param mode Build mode - SND_SCTL_xxxx
* \ result zero if success , otherwise a negative error code
*/
2001-11-19 08:14:21 +00:00
int snd_sctl_build ( snd_sctl_t * * sctl , snd_ctl_t * handle , snd_config_t * conf , snd_config_t * private_data , int mode )
2001-06-11 13:35:48 +00:00
{
snd_sctl_t * h ;
snd_config_iterator_t i , next ;
2013-02-11 12:28:45 +01:00
int err , quit = 0 ;
2001-06-11 13:35:48 +00:00
assert ( sctl ) ;
2001-06-15 08:47:59 +00:00
assert ( handle ) ;
2001-06-11 13:35:48 +00:00
assert ( conf ) ;
2001-12-29 22:28:35 +00:00
* sctl = NULL ;
2001-06-11 13:35:48 +00:00
if ( snd_config_get_type ( conf ) ! = SND_CONFIG_TYPE_COMPOUND )
return - EINVAL ;
h = calloc ( 1 , sizeof ( * h ) ) ;
if ( ! h ) {
if ( mode & SND_SCTL_NOFREE )
return - ENOMEM ;
snd_ctl_close ( handle ) ;
return - ENOMEM ;
}
h - > mode = mode ;
h - > ctl = handle ;
INIT_LIST_HEAD ( & h - > elems ) ;
snd_config_for_each ( i , next , conf ) {
snd_config_t * n = snd_config_iterator_entry ( i ) ;
2013-02-11 12:28:45 +01:00
err = add_elem ( h , n , private_data , & quit ) ;
2001-06-11 13:35:48 +00:00
if ( err < 0 ) {
free_elems ( h ) ;
return err ;
}
2013-02-11 12:28:45 +01:00
if ( quit )
break ;
2001-06-11 13:35:48 +00:00
}
* sctl = h ;
2001-05-10 08:32:40 +00:00
return 0 ;
}
2001-06-11 13:35:48 +00:00
2001-07-11 15:48:27 +00:00
/**
* \ brief Free setup control handle
* \ param sctl Setup control handle
* \ result zero if success , otherwise a negative error code
*/
2001-06-11 13:35:48 +00:00
int snd_sctl_free ( snd_sctl_t * sctl )
{
assert ( sctl ) ;
return free_elems ( sctl ) ;
}