Merge tag 'v1.2.16.1' of https://github.com/alsa-project/alsa-lib into android-16.0
Release v1.2.16.1 Core - Release v1.2.16.1 Use Case Manager API - ucm: move optional file trace into uc_mgr_config_load_into - ucm: pass optional flag to config load functions to suppress spurious errors Configuration - conf: add missing return value check in parse_def() Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
commit
15c7354c4e
31 changed files with 1420 additions and 256 deletions
29
.github/workflows/label-automation.yml
vendored
Normal file
29
.github/workflows/label-automation.yml
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Example workflow for using the label commenter
|
||||
# Place this file in your repository at: .github/workflows/label-automation.yml
|
||||
#
|
||||
# You also need to create .github/label-descriptions.yml in your repository
|
||||
# Example config: https://github.com/alsa-project/github-workflows/blob/main/config/label-descriptions.yml
|
||||
|
||||
name: Label Automation
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled, unlabeled]
|
||||
pull_request:
|
||||
types: [labeled, unlabeled]
|
||||
pull_request_target:
|
||||
types: [labeled, unlabeled]
|
||||
|
||||
jobs:
|
||||
handle-label:
|
||||
uses: alsa-project/github-workflows/.github/workflows/label-commenter.yml@main
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
with:
|
||||
# Path to your local config file OR URL to remote config (required)
|
||||
config-path: 'https://raw.githubusercontent.com/alsa-project/github-workflows/main/config/label-descriptions.yml'
|
||||
# Local file example: '.github/label-descriptions.yml'
|
||||
# Remote URL example: 'https://raw.githubusercontent.com/alsa-project/github-workflows/main/config/label-descriptions.yml'
|
||||
secrets: inherit
|
||||
35
.github/workflows/pr-validation.yml
vendored
Normal file
35
.github/workflows/pr-validation.yml
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Example workflow for automatic Signed-off-by validation
|
||||
# Place this file in your repository at: .github/workflows/pr-validation.yml
|
||||
#
|
||||
# You also need to create .github/label-descriptions.yml in your repository
|
||||
# Example config: https://github.com/alsa-project/github-workflows/blob/main/config/label-descriptions.yml
|
||||
|
||||
name: PR Validation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'Pull Request number to validate'
|
||||
required: true
|
||||
type: number
|
||||
|
||||
jobs:
|
||||
validate-commits:
|
||||
uses: alsa-project/github-workflows/.github/workflows/sob-validator.yml@main
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
with:
|
||||
# Path to your local config file OR URL to remote config (required)
|
||||
config-path: 'https://raw.githubusercontent.com/alsa-project/github-workflows/main/config/label-descriptions.yml'
|
||||
# Local file example: '.github/label-descriptions.yml'
|
||||
# Remote URL example: 'https://raw.githubusercontent.com/alsa-project/github-workflows/main/config/label-descriptions.yml'
|
||||
sob-label: 'signed off by'
|
||||
pr-number: ${{ github.event_name == 'workflow_dispatch' && format('{0}', inputs.pr_number) || '' }}
|
||||
secrets: inherit
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,4 +1,3 @@
|
|||
.*
|
||||
!.travis.yml
|
||||
configure
|
||||
config.log
|
||||
|
|
|
|||
|
|
@ -15,19 +15,19 @@ cc_library_shared {
|
|||
],
|
||||
exclude_srcs: [
|
||||
"src/control/ctl_symbols_list.c",
|
||||
"src/pcm/scopes/*.c",
|
||||
"src/pcm/pcm_dmix_generic.c",
|
||||
"src/pcm/pcm_dmix_i386.c",
|
||||
"src/pcm/pcm_dmix_x86_64.c",
|
||||
"src/pcm/pcm_symbols_list.c",
|
||||
"src/pcm/scopes/*.c",
|
||||
],
|
||||
cflags: [
|
||||
"-O2",
|
||||
"-DPIC",
|
||||
"-fPIC",
|
||||
"-O2",
|
||||
"-Wno-pointer-arith",
|
||||
"-Wno-pointer-bool-conversion",
|
||||
"-Wno-single-bit-bitfield-constant-conversion",
|
||||
"-fPIC",
|
||||
],
|
||||
required: ["libasound-config"],
|
||||
}
|
||||
|
|
|
|||
43
configure.ac
43
configure.ac
|
|
@ -1,6 +1,6 @@
|
|||
dnl Process this file with autoconf to produce a configure script.
|
||||
AC_PREREQ(2.59)
|
||||
AC_INIT(alsa-lib, 1.2.15.3)
|
||||
AC_PREREQ([2.72])
|
||||
AC_INIT([alsa-lib], [1.2.16.1])
|
||||
|
||||
AC_CONFIG_SRCDIR([src/control/control.c])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
|
@ -33,23 +33,25 @@ AC_USE_SYSTEM_EXTENSIONS
|
|||
AC_PROG_INSTALL
|
||||
AC_PROG_LN_S
|
||||
AC_DISABLE_STATIC
|
||||
AC_LIBTOOL_DLOPEN
|
||||
AM_PROG_LIBTOOL
|
||||
_LT_SET_OPTION([LT_INIT],[dlopen])
|
||||
LT_INIT
|
||||
|
||||
CC_NOUNDEFINED
|
||||
|
||||
dnl Checks for header files.
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_INCLUDES_DEFAULT
|
||||
AC_PROG_EGREP
|
||||
|
||||
AC_CONFIG_HEADERS(include/config.h)
|
||||
|
||||
dnl Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_C_CONST
|
||||
AC_C_INLINE
|
||||
AC_HEADER_TIME
|
||||
AC_CHECK_HEADERS_ONCE([sys/time.h])
|
||||
|
||||
AC_CHECK_ATTRIBUTE_SYMVER
|
||||
|
||||
dnl Checks for library functions.
|
||||
AC_PROG_GCC_TRADITIONAL
|
||||
AC_CHECK_FUNCS([uselocale])
|
||||
AC_CHECK_FUNCS([eaccess])
|
||||
AC_CHECK_DECLS([closefrom])
|
||||
|
|
@ -251,10 +253,7 @@ fi
|
|||
|
||||
dnl Check for scandir64
|
||||
AC_MSG_CHECKING(for LFS calls)
|
||||
AC_TRY_LINK([#include <dirent.h>],
|
||||
[struct dirent64 a; ],
|
||||
[have_lfs=yes],
|
||||
[have_lfs=no])
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <dirent.h>]], [[struct dirent64 a; ]])],[have_lfs=yes],[have_lfs=no])
|
||||
if test "$have_lfs" = "yes"; then
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE([HAVE_LFS], 1, [Have LFS])
|
||||
|
|
@ -358,7 +357,7 @@ AC_ARG_ENABLE(resmgr,
|
|||
AC_MSG_RESULT($resmgr)
|
||||
if test "$resmgr" = "yes"; then
|
||||
AC_CHECK_LIB(resmgr, rsm_open_device,,
|
||||
AC_ERROR([Cannot find libresmgr]))
|
||||
AC_MSG_ERROR(Cannot find libresmgr))
|
||||
AC_DEFINE(SUPPORT_RESMGR, "1", [Support resmgr with alsa-lib])
|
||||
fi
|
||||
|
||||
|
|
@ -482,7 +481,7 @@ AC_SUBST(PYTHON_INCLUDES)
|
|||
|
||||
if test "$build_rawmidi" != "yes"; then
|
||||
if test "$build_seq" = "yes"; then
|
||||
AC_ERROR([Cannot enable sequencer without rawmidi])
|
||||
AC_MSG_ERROR(Cannot enable sequencer without rawmidi)
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
@ -534,22 +533,16 @@ dnl check atomics for pcm_meter
|
|||
AC_MSG_CHECKING([whether GCC supports builtin atomic intrinsics])
|
||||
if test -z "$gcc_have_atomics"; then
|
||||
gcc_have_atomics=no
|
||||
AC_TRY_LINK([],
|
||||
[int i;
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[int i;
|
||||
__atomic_load_n(&i, __ATOMIC_SEQ_CST);
|
||||
__atomic_add_fetch(&i, 0, __ATOMIC_SEQ_CST);
|
||||
],
|
||||
[gcc_have_atomics=yes],
|
||||
[gcc_have_atomics=no])
|
||||
]])],[gcc_have_atomics=yes],[gcc_have_atomics=no])
|
||||
fi
|
||||
AC_MSG_RESULT($gcc_have_atomics)
|
||||
|
||||
dnl check mmx register for pcm_dmix_i386
|
||||
|
||||
AC_TRY_LINK([],
|
||||
[__asm__ volatile ("" : : : "mm0");],
|
||||
[AC_DEFINE([HAVE_MMX], "1", [MMX technology is enabled])],
|
||||
[])
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[__asm__ volatile ("" : : : "mm0");]])],[AC_DEFINE([HAVE_MMX], "1", [MMX technology is enabled])],[])
|
||||
|
||||
PCM_PLUGIN_LIST="copy linear route mulaw alaw adpcm rate plug multi shm file null empty share meter hooks lfloat ladspa dmix dshare dsnoop asym iec958 softvol extplug ioplug mmap_emul"
|
||||
|
||||
|
|
@ -734,9 +727,9 @@ AC_ARG_WITH(max-cards,
|
|||
AC_MSG_RESULT([$max_cards])
|
||||
|
||||
if test "$max_cards" -lt 1; then
|
||||
AC_ERROR([Invalid max cards $max_cards])
|
||||
AC_MSG_ERROR(Invalid max cards $max_cards)
|
||||
elif test "$max_cards" -gt 256; then
|
||||
AC_ERROR([Invalid max cards $max_cards])
|
||||
AC_MSG_ERROR(Invalid max cards $max_cards)
|
||||
fi
|
||||
AC_DEFINE_UNQUOTED(SND_MAX_CARDS, $max_cards, [Max number of cards])
|
||||
|
||||
|
|
@ -779,7 +772,7 @@ AC_CONFIG_FILES(Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg \
|
|||
test/Makefile test/lsb/Makefile \
|
||||
utils/Makefile utils/alsa-lib.spec utils/alsa.pc utils/alsa-topology.pc)
|
||||
|
||||
AC_OUTPUT()
|
||||
AC_OUTPUT
|
||||
|
||||
dnl Create asoundlib.h dynamically according to configure options
|
||||
echo "Creating asoundlib.h..."
|
||||
|
|
|
|||
|
|
@ -68,7 +68,8 @@ const char *snd_strerror(int errnum);
|
|||
#define SND_ILOG_UCM 10 /**< UCM API */
|
||||
#define SND_ILOG_TOPOLOGY 11 /**< topology API */
|
||||
#define SND_ILOG_ASERVER 12 /**< aserver */
|
||||
#define SND_ILOG_LAST SND_ILOG_ASERVER /**< last known value for interface */
|
||||
#define SND_ILOG_PCM_PARAMS 13 /**< PCM hw_params operations */
|
||||
#define SND_ILOG_LAST SND_ILOG_PCM_PARAMS /**< last known value for interface */
|
||||
|
||||
/**
|
||||
* \brief Log handler callback.
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@
|
|||
|
||||
#define SND_LIB_MAJOR 1 /**< major number of library version */
|
||||
#define SND_LIB_MINOR 2 /**< minor number of library version */
|
||||
#define SND_LIB_SUBMINOR 15 /**< subminor number of library version */
|
||||
#define SND_LIB_SUBMINOR 16 /**< subminor number of library version */
|
||||
#define SND_LIB_EXTRAVER 1000000 /**< extra version number, used mainly for betas */
|
||||
/** library version */
|
||||
#define SND_LIB_VER(maj, min, sub) (((maj)<<16)|((min)<<8)|(sub))
|
||||
#define SND_LIB_VERSION SND_LIB_VER(SND_LIB_MAJOR, SND_LIB_MINOR, SND_LIB_SUBMINOR)
|
||||
/** library version (string) */
|
||||
#define SND_LIB_VERSION_STR "1.2.15.3"
|
||||
#define SND_LIB_VERSION_STR "1.2.16.1"
|
||||
|
|
|
|||
|
|
@ -1485,6 +1485,10 @@ static int parse_def(snd_config_t *parent, input_t *input, int skip, int overrid
|
|||
endchr = ']';
|
||||
}
|
||||
c = get_nonwhite(input);
|
||||
if (c < 0) {
|
||||
err = c;
|
||||
goto __end;
|
||||
}
|
||||
if (c != endchr) {
|
||||
if (n)
|
||||
snd_config_delete(n);
|
||||
|
|
@ -4443,7 +4447,7 @@ int snd_config_hook_load_for_all_cards(snd_config_t *root, snd_config_t *config,
|
|||
goto __err;
|
||||
}
|
||||
while (1) {
|
||||
char *s = strchr(driver, '.');
|
||||
const char *s = strchr(driver, '.');
|
||||
if (s == NULL)
|
||||
break;
|
||||
driver = s + 1;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -97,8 +98,11 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
snd_ctl_t *child;
|
||||
int numid_remap_active;
|
||||
bool list_complete;
|
||||
bool numid_remap_active;
|
||||
unsigned int numid_app_last;
|
||||
unsigned int list_first;
|
||||
unsigned int list_last;
|
||||
|
||||
size_t numid_items;
|
||||
size_t numid_alloc;
|
||||
|
|
@ -125,6 +129,8 @@ typedef struct {
|
|||
} snd_ctl_remap_t;
|
||||
#endif
|
||||
|
||||
static int remap_load_list(snd_ctl_remap_t *priv);
|
||||
|
||||
static snd_ctl_numid_t *remap_numid_temp(snd_ctl_remap_t *priv, unsigned int numid)
|
||||
{
|
||||
priv->numid_temp.numid_child = numid;
|
||||
|
|
@ -137,6 +143,8 @@ static snd_ctl_numid_t *remap_find_numid_app(snd_ctl_remap_t *priv, unsigned int
|
|||
snd_ctl_numid_t *numid;
|
||||
size_t count;
|
||||
|
||||
if (numid_app == 0)
|
||||
return NULL;
|
||||
if (!priv->numid_remap_active)
|
||||
return remap_numid_temp(priv, numid_app);
|
||||
numid = priv->numid;
|
||||
|
|
@ -151,6 +159,8 @@ static snd_ctl_numid_t *remap_numid_new(snd_ctl_remap_t *priv, unsigned int numi
|
|||
{
|
||||
snd_ctl_numid_t *numid;
|
||||
|
||||
if (numid_app == 0)
|
||||
return NULL;
|
||||
if (priv->numid_alloc == priv->numid_items) {
|
||||
numid = realloc(priv->numid, (priv->numid_alloc + 16) * sizeof(*numid));
|
||||
if (numid == NULL)
|
||||
|
|
@ -187,6 +197,8 @@ static snd_ctl_numid_t *remap_find_numid_child(snd_ctl_remap_t *priv, unsigned i
|
|||
snd_ctl_numid_t *numid;
|
||||
size_t count;
|
||||
|
||||
if (numid_child == 0)
|
||||
return NULL;
|
||||
if (!priv->numid_remap_active)
|
||||
return remap_numid_temp(priv, numid_child);
|
||||
numid = priv->numid;
|
||||
|
|
@ -282,8 +294,11 @@ static int remap_id_to_child(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id, snd_c
|
|||
{
|
||||
snd_ctl_remap_id_t *rid;
|
||||
snd_ctl_numid_t *numid;
|
||||
bool reloaded = false;
|
||||
int err;
|
||||
|
||||
debug_id(id, "%s enter\n", __func__);
|
||||
_retry:
|
||||
rid = remap_find_id_app(priv, id);
|
||||
if (rid) {
|
||||
if (rid->id_app.numid == 0) {
|
||||
|
|
@ -295,13 +310,19 @@ static int remap_id_to_child(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id, snd_c
|
|||
}
|
||||
*id = rid->id_child;
|
||||
} else {
|
||||
if (remap_find_id_child(priv, id))
|
||||
return -ENOENT;
|
||||
numid = remap_find_numid_app(priv, id->numid);
|
||||
if (numid)
|
||||
if (numid) {
|
||||
id->numid = numid->numid_child;
|
||||
else
|
||||
id->numid = 0;
|
||||
} else {
|
||||
if (reloaded || priv->list_complete)
|
||||
return -ENOENT;
|
||||
/* build whole numid mapping */
|
||||
err = remap_load_list(priv);
|
||||
if (err < 0)
|
||||
return err;
|
||||
reloaded = true;
|
||||
goto _retry;
|
||||
}
|
||||
}
|
||||
*_rid = rid;
|
||||
debug_id(id, "%s leave\n", __func__);
|
||||
|
|
@ -329,6 +350,7 @@ static int remap_id_to_app(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id, snd_ctl
|
|||
id->numid = numid->numid_app;
|
||||
}
|
||||
}
|
||||
debug_id(id, "%s rid %p\n", __func__, rid);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -466,9 +488,8 @@ static int snd_ctl_remap_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info)
|
|||
return snd_ctl_card_info(priv->child, info);
|
||||
}
|
||||
|
||||
static int snd_ctl_remap_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list)
|
||||
static int _snd_ctl_remap_elem_list(snd_ctl_remap_t *priv, snd_ctl_elem_list_t *list)
|
||||
{
|
||||
snd_ctl_remap_t *priv = ctl->private_data;
|
||||
snd_ctl_elem_id_t *id;
|
||||
snd_ctl_remap_id_t *rid;
|
||||
snd_ctl_numid_t *numid;
|
||||
|
|
@ -483,13 +504,17 @@ static int snd_ctl_remap_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list)
|
|||
id = &list->pids[index];
|
||||
rid = remap_find_id_child(priv, id);
|
||||
if (rid) {
|
||||
rid->id_app.numid = id->numid;
|
||||
*id = rid->id_app;
|
||||
assert(id->numid > 0);
|
||||
rid->id_child.numid = id->numid;
|
||||
}
|
||||
numid = remap_find_numid_child(priv, id->numid);
|
||||
if (numid == NULL)
|
||||
return -EIO;
|
||||
id->numid = numid->numid_app;
|
||||
if (rid) {
|
||||
rid->id_app.numid = id->numid;
|
||||
*id = rid->id_app;
|
||||
}
|
||||
}
|
||||
if (list->offset >= list->count + priv->map_items + priv->sync_switch_items)
|
||||
return 0;
|
||||
|
|
@ -510,9 +535,39 @@ static int snd_ctl_remap_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list)
|
|||
}
|
||||
}
|
||||
list->count += priv->map_items + priv->sync_switch_items;
|
||||
if (list->offset < priv->list_first)
|
||||
priv->list_first = list->offset;
|
||||
if (list->offset == priv->list_last && list->offset + list->used > priv->list_last)
|
||||
priv->list_last = list->offset + list->used;
|
||||
priv->list_complete = priv->list_first == 0 && list->count == priv->list_last;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ctl_remap_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list)
|
||||
{
|
||||
snd_ctl_remap_t *priv = ctl->private_data;
|
||||
|
||||
return _snd_ctl_remap_elem_list(priv, list);
|
||||
}
|
||||
|
||||
static int remap_load_list(snd_ctl_remap_t *remap)
|
||||
{
|
||||
snd_ctl_elem_list_t list;
|
||||
int err = 0;
|
||||
|
||||
memset(&list, 0, sizeof(list));
|
||||
do {
|
||||
err = _snd_ctl_remap_elem_list(remap, &list);
|
||||
if (err < 0)
|
||||
break;
|
||||
err = snd_ctl_elem_list_alloc_space(&list, list.count);
|
||||
if (err < 0)
|
||||
break;
|
||||
} while (list.count != list.used);
|
||||
snd_ctl_elem_list_free_space(&list);
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifndef DOC_HIDDEN
|
||||
#define ACCESS_BITS(bits) \
|
||||
(bits & (SNDRV_CTL_ELEM_ACCESS_READWRITE|\
|
||||
|
|
@ -1674,6 +1729,7 @@ int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *rema
|
|||
|
||||
priv->numid_remap_active = priv->map_items > 0 || priv->sync_items;
|
||||
|
||||
priv->list_first = UINT_MAX;
|
||||
priv->child = child;
|
||||
err = snd_ctl_new(&ctl, SND_CTL_TYPE_REMAP, name, mode);
|
||||
if (err < 0) {
|
||||
|
|
|
|||
|
|
@ -156,8 +156,10 @@ char *snd_ctl_ascii_elem_id_get(snd_ctl_elem_id_t *id)
|
|||
int __snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst, const char *str,
|
||||
const char **ret_ptr)
|
||||
{
|
||||
int c, size, numid;
|
||||
char buf[64];
|
||||
int c, size;
|
||||
int err = -EINVAL;
|
||||
long l;
|
||||
char *ptr;
|
||||
|
||||
while (isspace(*str))
|
||||
|
|
@ -168,12 +170,23 @@ int __snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst, const char *str,
|
|||
while (*str) {
|
||||
if (!strncasecmp(str, "numid=", 6)) {
|
||||
str += 6;
|
||||
numid = atoi(str);
|
||||
if (numid <= 0) {
|
||||
fprintf(stderr, "amixer: Invalid numid %d\n", numid);
|
||||
ptr = buf;
|
||||
size = 0;
|
||||
while (*str && *str != ',') {
|
||||
if (size < (int)sizeof(buf)) {
|
||||
*ptr++ = *str;
|
||||
size++;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
*ptr = '\0';
|
||||
if (safe_strtol(buf, &l) < 0)
|
||||
l = -1;
|
||||
if (l <= 0 || l >= INT32_MAX) {
|
||||
snd_error(CONTROL, "Invalid numid %ld (%s)", l, buf);
|
||||
goto out;
|
||||
}
|
||||
snd_ctl_elem_id_set_numid(dst, atoi(str));
|
||||
snd_ctl_elem_id_set_numid(dst, (int)l);
|
||||
while (isdigit(*str))
|
||||
str++;
|
||||
} else if (!strncasecmp(str, "iface=", 6)) {
|
||||
|
|
@ -200,7 +213,6 @@ int __snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst, const char *str,
|
|||
goto out;
|
||||
}
|
||||
} else if (!strncasecmp(str, "name=", 5)) {
|
||||
char buf[64];
|
||||
str += 5;
|
||||
ptr = buf;
|
||||
size = 0;
|
||||
|
|
|
|||
|
|
@ -246,17 +246,16 @@ int snd_tlv_convert_to_dB(unsigned int *tlv, long rangemin, long rangemax,
|
|||
int mindb, maxdb;
|
||||
mindb = tlv[SNDRV_CTL_TLVO_DB_MINMAX_MIN];
|
||||
maxdb = tlv[SNDRV_CTL_TLVO_DB_MINMAX_MAX];
|
||||
if (rangemax <= rangemin)
|
||||
*db_gain = mindb;
|
||||
if (volume <= rangemin || rangemax <= rangemin) {
|
||||
if (type == SND_CTL_TLVT_DB_MINMAX_MUTE)
|
||||
*db_gain = SND_CTL_TLV_DB_GAIN_MUTE;
|
||||
else
|
||||
*db_gain = mindb;
|
||||
} else if (volume >= rangemax)
|
||||
*db_gain = maxdb;
|
||||
else
|
||||
*db_gain = (maxdb - mindb) * (volume - rangemin) /
|
||||
(rangemax - rangemin) + mindb;
|
||||
if (*db_gain < mindb)
|
||||
*db_gain = mindb;
|
||||
if (*db_gain > maxdb)
|
||||
*db_gain = maxdb;
|
||||
if (type == SND_CTL_TLVT_DB_MINMAX_MUTE && *db_gain == mindb)
|
||||
*db_gain = SND_CTL_TLV_DB_GAIN_MUTE;
|
||||
return 0;
|
||||
}
|
||||
#ifndef HAVE_SOFT_FLOAT
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ static const char *snd_ilog_interface_names[SND_ILOG_LAST + 1] = {
|
|||
[SND_ILOG_UCM] = "ucm",
|
||||
[SND_ILOG_TOPOLOGY] = "topology",
|
||||
[SND_ILOG_ASERVER] = "aserver",
|
||||
[SND_ILOG_PCM_PARAMS] = "pcmpar",
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -123,7 +124,7 @@ static const char *snd_ilog_interface_names[SND_ILOG_LAST + 1] = {
|
|||
*/
|
||||
const char *snd_lib_log_priority(int prio)
|
||||
{
|
||||
if (prio >= 0 && prio <= SND_LOG_TRACE)
|
||||
if (prio >= 0 && prio <= SND_LOG_LAST)
|
||||
return snd_log_prio_names[prio];
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -135,7 +136,7 @@ const char *snd_lib_log_priority(int prio)
|
|||
*/
|
||||
const char *snd_lib_log_interface(int interface)
|
||||
{
|
||||
if (interface >= 0 && interface <= SND_ILOG_TOPOLOGY)
|
||||
if (interface >= 0 && interface <= SND_ILOG_LAST)
|
||||
return snd_ilog_interface_names[interface];
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7824,7 +7824,7 @@ int snd_pcm_slave_conf(snd_config_t *root, snd_config_t *conf,
|
|||
int flags;
|
||||
void *ptr;
|
||||
int present;
|
||||
} fields[count];
|
||||
} fields[count > 0 ? count : 1];
|
||||
unsigned int k;
|
||||
snd_config_t *pcm_conf = NULL;
|
||||
int err;
|
||||
|
|
|
|||
|
|
@ -22,37 +22,42 @@
|
|||
#include "pcm_local.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
/*
|
||||
* dump hw_params when $LIBASOUND_DEBUG is set to >= 1
|
||||
*/
|
||||
static void dump_hw_params(snd_pcm_hw_params_t *params, const char *type,
|
||||
snd_pcm_hw_param_t var, unsigned int val, int err)
|
||||
{
|
||||
const char *verbose = getenv("LIBASOUND_DEBUG");
|
||||
snd_output_t *out;
|
||||
const char *s;
|
||||
char *p, *buf;
|
||||
|
||||
if (! verbose || ! *verbose || atoi(verbose) < 1)
|
||||
if (!snd_lib_log_filter(SND_LOG_DEBUG, SND_ILOG_PCM_PARAMS, NULL))
|
||||
return;
|
||||
if (snd_output_stdio_attach(&out, stderr, 0) < 0)
|
||||
return;
|
||||
fprintf(stderr, "ALSA ERROR hw_params: %s (%s)\n",
|
||||
type, snd_pcm_hw_param_name(var));
|
||||
fprintf(stderr, " value = ");
|
||||
switch (var) {
|
||||
case SND_PCM_HW_PARAM_ACCESS:
|
||||
fprintf(stderr, "%s", snd_pcm_access_name(val));
|
||||
s = snd_pcm_access_name(val);
|
||||
break;
|
||||
case SND_PCM_HW_PARAM_FORMAT:
|
||||
fprintf(stderr, "%s", snd_pcm_format_name(val));
|
||||
s = snd_pcm_format_name(val);
|
||||
break;
|
||||
case SND_PCM_HW_PARAM_SUBFORMAT:
|
||||
fprintf(stderr, "%s", snd_pcm_subformat_name(val));
|
||||
s = snd_pcm_subformat_name(val);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%u", val);
|
||||
s = NULL;
|
||||
}
|
||||
fprintf(stderr, " : %s\n", snd_strerror(err));
|
||||
if (snd_output_buffer_open(&out) < 0)
|
||||
return;
|
||||
snd_pcm_hw_params_dump(params, out);
|
||||
snd_output_putc(out, '\0');
|
||||
snd_output_buffer_string(out, &buf);
|
||||
for (p = buf; *p; p++)
|
||||
if (*p == '\n')
|
||||
*p = '|';
|
||||
if (s)
|
||||
snd_debug(PCM_PARAMS, "hw_params: %s (%s), value = %s : %s {%s}",
|
||||
type, snd_pcm_hw_param_name(var), s, snd_strerror(err), buf);
|
||||
else
|
||||
snd_debug(PCM_PARAMS, "hw_params: %s (%s), value = %u : %s {%s}",
|
||||
type, snd_pcm_hw_param_name(var), val, snd_strerror(err), buf);
|
||||
snd_output_close(out);
|
||||
}
|
||||
#else
|
||||
|
|
@ -1220,11 +1225,12 @@ void snd_pcm_hw_param_dump(const snd_pcm_hw_params_t *params,
|
|||
if (hw_is_mask(var)) {
|
||||
const snd_mask_t *mask = hw_param_mask_c(params, var);
|
||||
if (snd_mask_empty(mask))
|
||||
snd_output_puts(out, " NONE");
|
||||
snd_output_puts(out, "NONE");
|
||||
else if (snd_mask_full(mask))
|
||||
snd_output_puts(out, " ALL");
|
||||
snd_output_puts(out, "ALL");
|
||||
else {
|
||||
unsigned int k;
|
||||
int sep = 0;
|
||||
for (k = 0; k <= SND_MASK_MAX; ++k) {
|
||||
if (snd_mask_test(mask, k)) {
|
||||
const char *s;
|
||||
|
|
@ -1243,8 +1249,10 @@ void snd_pcm_hw_param_dump(const snd_pcm_hw_params_t *params,
|
|||
s = NULL;
|
||||
}
|
||||
if (s) {
|
||||
snd_output_putc(out, ' ');
|
||||
if (sep)
|
||||
snd_output_putc(out, ' ');
|
||||
snd_output_puts(out, s);
|
||||
sep = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -811,11 +811,15 @@ size_t snd_rawmidi_params_get_avail_min(const snd_rawmidi_params_t *params)
|
|||
}
|
||||
|
||||
/**
|
||||
* \brief set no-active-sensing action on snd_rawmidi_close()
|
||||
* \brief control whether an Active Sensing byte is sent on output close
|
||||
* \param rawmidi RawMidi handle
|
||||
* \param params pointer to snd_rawmidi_params_t structure
|
||||
* \param val value: 0 = enable to send the active sensing message, 1 = disable
|
||||
* \param val 0 = send Active Sensing (0xFE) when the output stream is closed,
|
||||
* 1 = suppress it (default)
|
||||
* \return 0 on success otherwise a negative error code
|
||||
*
|
||||
* This setting applies to the \b output direction only. It does not filter
|
||||
* or suppress incoming Active Sensing messages received on an input stream.
|
||||
*/
|
||||
#ifndef DOXYGEN
|
||||
int snd_rawmidi_params_set_no_active_sensing(snd_rawmidi_t *rawmidi ATTRIBUTE_UNUSED, snd_rawmidi_params_t *params, int val)
|
||||
|
|
@ -831,7 +835,7 @@ int snd_rawmidi_params_set_no_active_sensing(snd_rawmidi_t *rawmidi, snd_rawmidi
|
|||
/**
|
||||
* \brief get no-active-sensing action status
|
||||
* \param params pointer to snd_rawmidi_params_t structure
|
||||
* \return the current status (0 = enable, 1 = disable the active sensing message)
|
||||
* \return 0 if an Active Sensing byte will be sent on output close, 1 if suppressed
|
||||
*/
|
||||
int snd_rawmidi_params_get_no_active_sensing(const snd_rawmidi_params_t *params)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -424,8 +424,8 @@ int snd_seq_sync_output_queue(snd_seq_t *seq)
|
|||
*/
|
||||
int snd_seq_parse_address(snd_seq_t *seq, snd_seq_addr_t *addr, const char *arg)
|
||||
{
|
||||
char *p, *buf;
|
||||
const char *s;
|
||||
char *buf;
|
||||
const char *p, *s;
|
||||
char c;
|
||||
long client, port = 0;
|
||||
int len;
|
||||
|
|
|
|||
|
|
@ -1250,6 +1250,11 @@ int tplg_decode_control_mixer1(snd_tplg_t *tplg,
|
|||
if (mc->num_channels > 0) {
|
||||
map = tplg_calloc(heap, sizeof(*map));
|
||||
map->num_channels = mc->num_channels;
|
||||
if (map->num_channels > SND_TPLG_MAX_CHAN ||
|
||||
map->num_channels > SND_SOC_TPLG_MAX_CHAN) {
|
||||
snd_error(TOPOLOGY, "mixer: unexpected channel count %d", map->num_channels);
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; i < map->num_channels; i++) {
|
||||
map->channel[i].reg = mc->channel[i].reg;
|
||||
map->channel[i].shift = mc->channel[i].shift;
|
||||
|
|
|
|||
|
|
@ -983,7 +983,7 @@ next:
|
|||
bin, size2);
|
||||
break;
|
||||
case SND_SOC_TPLG_TYPE_ENUM:
|
||||
et = tplg_calloc(&heap, sizeof(*mt));
|
||||
et = tplg_calloc(&heap, sizeof(*et));
|
||||
if (et == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto retval;
|
||||
|
|
|
|||
|
|
@ -25,16 +25,23 @@ static const char *const snd_pcm_rate_names[] = {
|
|||
RATE(5512),
|
||||
RATE(8000),
|
||||
RATE(11025),
|
||||
RATE(12000),
|
||||
RATE(16000),
|
||||
RATE(22050),
|
||||
RATE(24000),
|
||||
RATE(32000),
|
||||
RATE(44100),
|
||||
RATE(48000),
|
||||
RATE(64000),
|
||||
RATE(88200),
|
||||
RATE(96000),
|
||||
RATE(128000),
|
||||
RATE(176400),
|
||||
RATE(192000),
|
||||
RATE(352800),
|
||||
RATE(384000),
|
||||
RATE(705600),
|
||||
RATE(768000),
|
||||
RATE(CONTINUOUS),
|
||||
RATE(KNOT),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -55,6 +55,13 @@ typedef enum _snd_pcm_rates {
|
|||
SND_PCM_RATE_96000,
|
||||
SND_PCM_RATE_176400,
|
||||
SND_PCM_RATE_192000,
|
||||
SND_PCM_RATE_352800,
|
||||
SND_PCM_RATE_384000,
|
||||
SND_PCM_RATE_705600,
|
||||
SND_PCM_RATE_768000,
|
||||
SND_PCM_RATE_12000,
|
||||
SND_PCM_RATE_24000,
|
||||
SND_PCM_RATE_128000,
|
||||
SND_PCM_RATE_CONTINUOUS = 30,
|
||||
SND_PCM_RATE_KNOT = 31,
|
||||
SND_PCM_RATE_LAST = SND_PCM_RATE_KNOT,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
EXTRA_LTLIBRARIES = libucm.la
|
||||
|
||||
libucm_la_SOURCES = utils.c parser.c ucm_cond.c ucm_subs.c ucm_include.c \
|
||||
ucm_regex.c ucm_exec.c main.c
|
||||
ucm_regex.c ucm_repeat.c ucm_exec.c main.c
|
||||
|
||||
noinst_HEADERS = ucm_local.h ucm_confdoc.h
|
||||
|
||||
|
|
|
|||
|
|
@ -1702,7 +1702,7 @@ const char *parse_open_variables(snd_use_case_mgr_t *uc_mgr, const char *name)
|
|||
{
|
||||
const char *end, *id;
|
||||
char *args, *var;
|
||||
snd_config_t *cfg, *n;
|
||||
snd_config_t *cfg = NULL, *n;
|
||||
snd_config_iterator_t i, next;
|
||||
char vname[128];
|
||||
size_t l;
|
||||
|
|
@ -1739,7 +1739,8 @@ const char *parse_open_variables(snd_use_case_mgr_t *uc_mgr, const char *name)
|
|||
}
|
||||
|
||||
skip:
|
||||
snd_config_delete(cfg);
|
||||
if (cfg)
|
||||
snd_config_delete(cfg);
|
||||
return end + 3;
|
||||
}
|
||||
|
||||
|
|
@ -1780,7 +1781,7 @@ int snd_use_case_mgr_open(snd_use_case_mgr_t **uc_mgr,
|
|||
card_name = parse_open_variables(mgr, card_name);
|
||||
|
||||
/* Application developers: This argument is not supposed to be set for standard applications. */
|
||||
if (uc_mgr_get_variable(mgr, "@InBoot"))
|
||||
if (uc_mgr_get_variable(mgr, "@InBoot", false))
|
||||
mgr->in_boot = true;
|
||||
|
||||
err = uc_mgr_card_open(mgr);
|
||||
|
|
@ -2378,7 +2379,8 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr,
|
|||
const char *identifier,
|
||||
const char **list[])
|
||||
{
|
||||
char *str, *str1;
|
||||
char *str;
|
||||
const char *str1;
|
||||
int err, i;
|
||||
|
||||
if (uc_mgr == NULL || identifier == NULL) {
|
||||
|
|
@ -2711,7 +2713,8 @@ int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr,
|
|||
const char *identifier,
|
||||
long *value)
|
||||
{
|
||||
char *str, *str1;
|
||||
char *str;
|
||||
const char *str1;
|
||||
int err;
|
||||
|
||||
pthread_mutex_lock(&uc_mgr->mutex);
|
||||
|
|
@ -3013,7 +3016,8 @@ int snd_use_case_set(snd_use_case_mgr_t *uc_mgr,
|
|||
const char *identifier,
|
||||
const char *value)
|
||||
{
|
||||
char *str, *str1;
|
||||
char *str;
|
||||
const char *str1;
|
||||
int err = 0;
|
||||
|
||||
snd_trace(UCM, "{API call} set '%s'='%s'", identifier, value);
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ static void ucm_filename(char *fn, size_t fn_len, long version,
|
|||
*
|
||||
*/
|
||||
int uc_mgr_config_load_file(snd_use_case_mgr_t *uc_mgr,
|
||||
const char *file, snd_config_t **cfg)
|
||||
const char *file, snd_config_t **cfg, bool optional)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
int err;
|
||||
|
|
@ -74,9 +74,10 @@ int uc_mgr_config_load_file(snd_use_case_mgr_t *uc_mgr,
|
|||
ucm_filename(filename, sizeof(filename), uc_mgr->conf_format,
|
||||
file[0] == '/' ? NULL : uc_mgr->conf_dir_name,
|
||||
file);
|
||||
err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg);
|
||||
err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg, optional);
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "failed to open file %s: %d", filename, err);
|
||||
if (!optional || (err != -ENOENT && err != -EACCES))
|
||||
snd_error(UCM, "failed to open file %s: %d", filename, err);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -391,7 +392,7 @@ static int evaluate_define(snd_use_case_mgr_t *uc_mgr,
|
|||
snd_config_iterator_t i, next;
|
||||
snd_config_t *d, *n;
|
||||
const char *id;
|
||||
char *var, *s;
|
||||
char *var, *s, *sid;
|
||||
int err;
|
||||
|
||||
err = snd_config_search(cfg, "Define", &d);
|
||||
|
|
@ -427,8 +428,18 @@ static int evaluate_define(snd_use_case_mgr_t *uc_mgr,
|
|||
snd_error(UCM, "value names starting with '@' are reserved for application variables");
|
||||
return -EINVAL;
|
||||
}
|
||||
err = uc_mgr_set_variable(uc_mgr, id, s);
|
||||
sid = (char *)id;
|
||||
if (uc_mgr->conf_format >= 9) {
|
||||
err = uc_mgr_get_substituted_value(uc_mgr, &sid, id);
|
||||
if (err < 0) {
|
||||
free(s);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
err = uc_mgr_set_variable(uc_mgr, sid, s);
|
||||
free(s);
|
||||
if (id != sid)
|
||||
free(sid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
|
@ -495,7 +506,15 @@ static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr,
|
|||
err = snd_config_get_string(args, &s);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_config_load_string(&a, s, 0);
|
||||
if (uc_mgr->conf_format < 9) {
|
||||
err = snd_config_load_string(&a, s, 0);
|
||||
} else {
|
||||
err = uc_mgr_get_substituted_value(uc_mgr, &var2, s);
|
||||
if (err >= 0) {
|
||||
err = snd_config_load_string(&a, var2, 0);
|
||||
free(var2);
|
||||
}
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else if (snd_config_get_type(args) != SND_CONFIG_TYPE_COMPOUND) {
|
||||
|
|
@ -509,7 +528,7 @@ static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr,
|
|||
if (err < 0)
|
||||
goto __err_path;
|
||||
snprintf(name, sizeof(name), "__%s", id);
|
||||
if (uc_mgr_get_variable(uc_mgr, name)) {
|
||||
if (uc_mgr_get_variable(uc_mgr, name, false)) {
|
||||
snd_error(UCM, "Macro argument '%s' is already defined", name);
|
||||
goto __err_path;
|
||||
}
|
||||
|
|
@ -711,9 +730,9 @@ int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
|
|||
snd_config_t *cfg)
|
||||
{
|
||||
long iterations = 10000;
|
||||
int err1 = 0, err2 = 0, err3 = 0, err4 = 0, err5 = 0;
|
||||
int err1 = 0, err2 = 0, err3 = 0, err4 = 0, err5 = 0, err6 = 0;
|
||||
|
||||
while (err1 == 0 || err2 == 0 || err3 == 0 || err4 == 0 || err5 == 0) {
|
||||
while (err1 == 0 || err2 == 0 || err3 == 0 || err4 == 0 || err5 == 0 || err6 == 0) {
|
||||
if (iterations == 0) {
|
||||
snd_error(UCM, "Maximal inplace evaluation iterations number reached (recursive references?)");
|
||||
return -EINVAL;
|
||||
|
|
@ -747,9 +766,12 @@ int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
|
|||
return err4;
|
||||
if (err4 == 0)
|
||||
continue;
|
||||
err5 = evaluate_condition(uc_mgr, cfg);
|
||||
err5 = uc_mgr_evaluate_repeat(uc_mgr, cfg);
|
||||
if (err5 < 0)
|
||||
return err5;
|
||||
err6 = evaluate_condition(uc_mgr, cfg);
|
||||
if (err6 < 0)
|
||||
return err6;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -804,7 +826,7 @@ static int parse_libconfig1(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
|
|||
if (file) {
|
||||
if (substfile) {
|
||||
snd_config_t *cfg;
|
||||
err = uc_mgr_config_load(uc_mgr->conf_format, file, &cfg);
|
||||
err = uc_mgr_config_load_file(uc_mgr, file, &cfg, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = uc_mgr_substitute_tree(uc_mgr, cfg);
|
||||
|
|
@ -823,7 +845,7 @@ static int parse_libconfig1(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
|
|||
ucm_filename(filename, sizeof(filename), uc_mgr->conf_format,
|
||||
file[0] == '/' ? NULL : uc_mgr->conf_dir_name,
|
||||
file);
|
||||
err = uc_mgr_config_load_into(uc_mgr->conf_format, filename, uc_mgr->local_config);
|
||||
err = uc_mgr_config_load_into(uc_mgr->conf_format, filename, uc_mgr->local_config, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
|
@ -2928,7 +2950,7 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
|
|||
if (file) {
|
||||
snd_config_t *cfg;
|
||||
/* load config from file */
|
||||
err = uc_mgr_config_load_file(uc_mgr, file, &cfg);
|
||||
err = uc_mgr_config_load_file(uc_mgr, file, &cfg, false);
|
||||
if (err < 0)
|
||||
goto __error;
|
||||
/* parse the config */
|
||||
|
|
@ -2996,7 +3018,7 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
|
|||
snd_config_t *cfg;
|
||||
const char *fname = vfile ? vfile : file;
|
||||
/* load config from file */
|
||||
err = uc_mgr_config_load_file(uc_mgr, fname, &cfg);
|
||||
err = uc_mgr_config_load_file(uc_mgr, fname, &cfg, false);
|
||||
if (err >= 0) {
|
||||
err = parse_verb_config(uc_mgr, id,
|
||||
vcomment ? vcomment : comment,
|
||||
|
|
@ -3555,7 +3577,7 @@ static int load_toplevel_config(snd_use_case_mgr_t *uc_mgr,
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
err = uc_mgr_config_load(2, filename, &tcfg);
|
||||
err = uc_mgr_config_load(2, filename, &tcfg, false);
|
||||
if (err < 0)
|
||||
goto __error;
|
||||
|
||||
|
|
@ -3565,7 +3587,7 @@ static int load_toplevel_config(snd_use_case_mgr_t *uc_mgr,
|
|||
if (err < 0)
|
||||
goto __error;
|
||||
|
||||
err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg);
|
||||
err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg, false);
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "could not parse configuration for card %s", uc_mgr->card_name);
|
||||
goto __error;
|
||||
|
|
@ -3771,7 +3793,7 @@ int uc_mgr_scan_master_configs(const char **_list[])
|
|||
#endif
|
||||
continue;
|
||||
|
||||
err = uc_mgr_config_load(2, filename, &cfg);
|
||||
err = uc_mgr_config_load(2, filename, &cfg, false);
|
||||
if (err < 0)
|
||||
goto __err;
|
||||
err = snd_config_search(cfg, "Syntax", &c);
|
||||
|
|
|
|||
|
|
@ -38,10 +38,70 @@ static int get_string(snd_config_t *compound, const char *key, const char **str)
|
|||
return snd_config_get_string(node, str);
|
||||
}
|
||||
|
||||
static int if_eval_string(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval)
|
||||
typedef int (*string_compare_t)(const char *s1, const char *s2);
|
||||
|
||||
static int compare_strings(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval,
|
||||
const char *key1, const char *key2,
|
||||
string_compare_t compare)
|
||||
{
|
||||
const char *string1 = NULL, *string2 = NULL;
|
||||
char *s1, *s2;
|
||||
int err, result;
|
||||
|
||||
err = get_string(eval, key1, &string1);
|
||||
if (err < 0 && err != -ENOENT) {
|
||||
snd_error(UCM, "String error (If.Condition.%s)", key1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = get_string(eval, key2, &string2);
|
||||
if (err < 0 && err != -ENOENT) {
|
||||
snd_error(UCM, "String error (If.Condition.%s)", key2);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!string1 && !string2)
|
||||
return -ENOENT; /* not found */
|
||||
|
||||
if (!string1) {
|
||||
snd_error(UCM, "If.Condition.%s not defined", key1);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!string2) {
|
||||
snd_error(UCM, "If.Condition.%s not defined", key2);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = uc_mgr_get_substituted_value(uc_mgr, &s1, string1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = uc_mgr_get_substituted_value(uc_mgr, &s2, string2);
|
||||
if (err < 0) {
|
||||
free(s1);
|
||||
return err;
|
||||
}
|
||||
|
||||
result = compare(s1, s2);
|
||||
free(s2);
|
||||
free(s1);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int string_equal(const char *s1, const char *s2)
|
||||
{
|
||||
return strcasecmp(s1, s2) == 0;
|
||||
}
|
||||
|
||||
static int string_contains(const char *s1, const char *s2)
|
||||
{
|
||||
return strstr(s1, s2) != NULL;
|
||||
}
|
||||
|
||||
static int if_eval_string(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval)
|
||||
{
|
||||
const char *string1 = NULL;
|
||||
char *s1;
|
||||
int err;
|
||||
|
||||
if (uc_mgr->conf_format >= 3) {
|
||||
|
|
@ -61,75 +121,13 @@ static int if_eval_string(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval)
|
|||
}
|
||||
}
|
||||
|
||||
err = get_string(eval, "String1", &string1);
|
||||
if (err < 0 && err != -ENOENT) {
|
||||
snd_error(UCM, "String error (If.Condition.String1)");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = get_string(eval, "String2", &string2);
|
||||
if (err < 0 && err != -ENOENT) {
|
||||
snd_error(UCM, "String error (If.Condition.String2)");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (string1 || string2) {
|
||||
if (string1 == NULL) {
|
||||
snd_error(UCM, "If.Condition.String1 not defined");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (string2 == NULL) {
|
||||
snd_error(UCM, "If.Condition.String2 not defined");
|
||||
return -EINVAL;
|
||||
}
|
||||
err = uc_mgr_get_substituted_value(uc_mgr, &s1, string1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = uc_mgr_get_substituted_value(uc_mgr, &s2, string2);
|
||||
if (err < 0) {
|
||||
free(s1);
|
||||
return err;
|
||||
}
|
||||
err = strcasecmp(s1, s2) == 0;
|
||||
free(s2);
|
||||
free(s1);
|
||||
err = compare_strings(uc_mgr, eval, "String1", "String2", string_equal);
|
||||
if (err != -ENOENT) /* -ENOENT means not found, continue checking */
|
||||
return err;
|
||||
}
|
||||
|
||||
err = get_string(eval, "Haystack", &string1);
|
||||
if (err < 0 && err != -ENOENT) {
|
||||
snd_error(UCM, "String error (If.Condition.Haystack)");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = get_string(eval, "Needle", &string2);
|
||||
if (err < 0 && err != -ENOENT) {
|
||||
snd_error(UCM, "String error (If.Condition.Needle)");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (string1 || string2) {
|
||||
if (string1 == NULL) {
|
||||
snd_error(UCM, "If.Condition.Haystack not defined");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (string2 == NULL) {
|
||||
snd_error(UCM, "If.Condition.Needle not defined");
|
||||
return -EINVAL;
|
||||
}
|
||||
err = uc_mgr_get_substituted_value(uc_mgr, &s1, string1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = uc_mgr_get_substituted_value(uc_mgr, &s2, string2);
|
||||
if (err < 0) {
|
||||
free(s1);
|
||||
return err;
|
||||
}
|
||||
err = strstr(s1, s2) != NULL;
|
||||
free(s2);
|
||||
free(s1);
|
||||
err = compare_strings(uc_mgr, eval, "Haystack", "Needle", string_contains);
|
||||
if (err != -ENOENT)
|
||||
return err;
|
||||
}
|
||||
|
||||
snd_error(UCM, "Unknown String condition arguments");
|
||||
return -EINVAL;
|
||||
|
|
@ -270,6 +268,80 @@ static int if_eval_control_exists(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int if_eval_integer(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval)
|
||||
{
|
||||
const char *value1_str = NULL, *value2_str = NULL, *operation = NULL;
|
||||
char *s1, *s2;
|
||||
long long val1, val2;
|
||||
int err, err1, err2;
|
||||
|
||||
if (uc_mgr->conf_format < 9) {
|
||||
snd_error(UCM, "Integer condition is supported in v9+ syntax");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = get_string(eval, "Operation", &operation);
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "Integer error (If.Condition.Operation)");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = get_string(eval, "Value1", &value1_str);
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "Integer error (If.Condition.Value1)");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = get_string(eval, "Value2", &value2_str);
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "Integer error (If.Condition.Value2)");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = uc_mgr_get_substituted_value(uc_mgr, &s1, value1_str);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = uc_mgr_get_substituted_value(uc_mgr, &s2, value2_str);
|
||||
if (err < 0) {
|
||||
free(s1);
|
||||
return err;
|
||||
}
|
||||
|
||||
err1 = safe_strtoll(s1, &val1);
|
||||
err2 = safe_strtoll(s2, &val2);
|
||||
|
||||
if (err1 < 0 || err2 < 0) {
|
||||
if (err1 < 0)
|
||||
snd_error(UCM, "Integer conversion error for Value1 '%s'", s1);
|
||||
if (err2 < 0)
|
||||
snd_error(UCM, "Integer conversion error for Value2 '%s'", s2);
|
||||
free(s2);
|
||||
free(s1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
free(s2);
|
||||
free(s1);
|
||||
|
||||
if (strcmp(operation, "==") == 0) {
|
||||
return val1 == val2;
|
||||
} else if (strcmp(operation, "!=") == 0) {
|
||||
return val1 != val2;
|
||||
} else if (strcmp(operation, "<") == 0) {
|
||||
return val1 < val2;
|
||||
} else if (strcmp(operation, ">") == 0) {
|
||||
return val1 > val2;
|
||||
} else if (strcmp(operation, "<=") == 0) {
|
||||
return val1 <= val2;
|
||||
} else if (strcmp(operation, ">=") == 0) {
|
||||
return val1 >= val2;
|
||||
} else {
|
||||
snd_error(UCM, "Integer unknown operation '%s'", operation);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int if_eval_path(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval)
|
||||
{
|
||||
const char *path, *mode = "";
|
||||
|
|
@ -365,6 +437,9 @@ static int if_eval(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval)
|
|||
if (strcmp(type, "Path") == 0)
|
||||
return if_eval_path(uc_mgr, eval);
|
||||
|
||||
if (strcmp(type, "Integer") == 0)
|
||||
return if_eval_integer(uc_mgr, eval);
|
||||
|
||||
snd_error(UCM, "unknown If.Condition.Type");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -377,7 +452,9 @@ static int if_eval_one(snd_use_case_mgr_t *uc_mgr,
|
|||
snd_config_t **prepend,
|
||||
snd_config_t **append)
|
||||
{
|
||||
snd_config_t *expr, *_true = NULL, *_false = NULL;
|
||||
snd_config_t *expr, *expr_eval = NULL, *_true = NULL, *_false = NULL;
|
||||
const char *s;
|
||||
char *s1;
|
||||
int err, has_condition;
|
||||
|
||||
*result = NULL;
|
||||
|
|
@ -392,73 +469,99 @@ static int if_eval_one(snd_use_case_mgr_t *uc_mgr,
|
|||
/* For syntax v8+, Condition is optional if Prepend or Append is present */
|
||||
has_condition = snd_config_search(cond, "Condition", &expr) >= 0;
|
||||
|
||||
if (has_condition && uc_mgr->conf_format >= 9 &&
|
||||
snd_config_get_type(expr) == SND_CONFIG_TYPE_STRING) {
|
||||
err = snd_config_get_string(expr, &s);
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "Condition string error (If)");
|
||||
return -EINVAL;
|
||||
}
|
||||
err = uc_mgr_get_substituted_value(uc_mgr, &s1, s);
|
||||
if (err >= 0) {
|
||||
err = snd_config_load_string(&expr_eval, s1, 0);
|
||||
free(s1);
|
||||
}
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "Condition string parse error (If)");
|
||||
return err;
|
||||
}
|
||||
expr = expr_eval;
|
||||
}
|
||||
|
||||
if (uc_mgr->conf_format >= 8) {
|
||||
/* Check for Prepend block */
|
||||
err = snd_config_search(cond, "Prepend", prepend);
|
||||
if (err < 0 && err != -ENOENT) {
|
||||
snd_error(UCM, "prepend block error (If)");
|
||||
return -EINVAL;
|
||||
goto __error;
|
||||
}
|
||||
|
||||
/* Check for Append block */
|
||||
err = snd_config_search(cond, "Append", append);
|
||||
if (err < 0 && err != -ENOENT) {
|
||||
snd_error(UCM, "append block error (If)");
|
||||
return -EINVAL;
|
||||
goto __error;
|
||||
}
|
||||
|
||||
/* If Prepend or Append is present, Condition can be omitted */
|
||||
if (!has_condition && (*prepend == NULL && *append == NULL)) {
|
||||
snd_error(UCM, "condition block expected (If)");
|
||||
return -EINVAL;
|
||||
goto __error;
|
||||
}
|
||||
} else {
|
||||
if (!has_condition) {
|
||||
snd_error(UCM, "condition block expected (If)");
|
||||
return -EINVAL;
|
||||
goto __error;
|
||||
}
|
||||
}
|
||||
|
||||
err = snd_config_search(cond, "True", &_true);
|
||||
if (err < 0 && err != -ENOENT) {
|
||||
snd_error(UCM, "true block error (If)");
|
||||
return -EINVAL;
|
||||
goto __error;
|
||||
}
|
||||
|
||||
err = snd_config_search(cond, "False", &_false);
|
||||
if (err < 0 && err != -ENOENT) {
|
||||
snd_error(UCM, "false block error (If)");
|
||||
return -EINVAL;
|
||||
goto __error;
|
||||
}
|
||||
|
||||
err = snd_config_search(cond, "Before", before);
|
||||
if (err < 0 && err != -ENOENT) {
|
||||
snd_error(UCM, "before block identifier error");
|
||||
return -EINVAL;
|
||||
goto __error;
|
||||
}
|
||||
|
||||
err = snd_config_search(cond, "After", after);
|
||||
if (err < 0 && err != -ENOENT) {
|
||||
snd_error(UCM, "before block identifier error");
|
||||
return -EINVAL;
|
||||
goto __error;
|
||||
}
|
||||
|
||||
/* Evaluate condition if present */
|
||||
if (has_condition) {
|
||||
err = if_eval(uc_mgr, expr);
|
||||
if (err < 0)
|
||||
goto __error;
|
||||
if (err > 0) {
|
||||
*result = _true;
|
||||
return 0;
|
||||
goto __return;
|
||||
} else if (err == 0) {
|
||||
*result = _false;
|
||||
return 0;
|
||||
} else {
|
||||
return err;
|
||||
goto __return;
|
||||
}
|
||||
}
|
||||
|
||||
/* If no condition (v8+ with Prepend/Append only), no result block */
|
||||
return 0;
|
||||
|
||||
__error:
|
||||
err = -EINVAL;
|
||||
__return:
|
||||
if (expr_eval)
|
||||
snd_config_delete(expr_eval);
|
||||
return err;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
|
|
|||
|
|
@ -587,8 +587,8 @@ Evaluation order | Configuration block | Evaluation restart
|
|||
2 | Include | Yes
|
||||
3 | Variant | Yes
|
||||
4 | Macro | Yes
|
||||
5 | If | Yes
|
||||
|
||||
5 | Repeat | Yes
|
||||
6 | If | Yes
|
||||
|
||||
### Substitutions
|
||||
|
||||
|
|
@ -616,6 +616,7 @@ ${var:\<str\>} | UCM parser variable (set using a _Define_ block)
|
|||
${eval:\<str\>} | Evaluate expression like *($var+2)/3* [**Syntax 5**]
|
||||
${find-card:\<str\>} | Find a card - see _Find card substitution_ section
|
||||
${find-device:\<str\>} | Find a device - see _Find device substitution_ section
|
||||
${info-card:\<str\>} | Get card information - see _Card info substitution_ section [**Syntax 9**]
|
||||
|
||||
General note: If two dollars '$$' instead one dolar '$' are used for the
|
||||
substitution identification, the error is ignored (e.g. file does not
|
||||
|
|
@ -662,6 +663,7 @@ Usage example:
|
|||
|
||||
~~~{.html}
|
||||
${find-card:field=name,regex='^acp$',return=number}
|
||||
${find-card:field=$FieldName,regex=$Pattern,return=number}
|
||||
~~~
|
||||
|
||||
Arguments:
|
||||
|
|
@ -669,8 +671,8 @@ Arguments:
|
|||
Argument | Description
|
||||
---------------------|-----------------------
|
||||
return | return value type (id, number), id is the default
|
||||
field | field for the lookup (id, driver, name, longname, mixername, components)
|
||||
regex | regex string for the field match
|
||||
field | field for the lookup (id, driver, name, longname, mixername, components) or variable name ($var) [**Syntax 9**]
|
||||
regex | regex string for the field match or variable name ($var) [**Syntax 9**]
|
||||
|
||||
#### Find device substitution
|
||||
|
||||
|
|
@ -678,16 +680,50 @@ Usage example:
|
|||
|
||||
~~~{.html}
|
||||
${find-device:type=pcm,field=name,regex='DMIC'}
|
||||
${find-device:type=$DevType,stream=$StreamType,field=$FieldName,regex=$Pattern}
|
||||
~~~
|
||||
|
||||
Arguments:
|
||||
|
||||
Argument | Description
|
||||
---------------------|-----------------------
|
||||
type | device type (pcm)
|
||||
stream | stream type (playback, capture), playback is default
|
||||
field | field for the lookup (id, name, subname)
|
||||
regex | regex string for the field match
|
||||
type | device type (pcm) or variable name ($var) [**Syntax 9**]
|
||||
stream | stream type (playback, capture), playback is default; variable name ($var) supported in **Syntax 9**
|
||||
field | field for the lookup (id, name, subname) or variable name ($var) [**Syntax 9**]
|
||||
regex | regex string for the field match or variable name ($var) [**Syntax 9**]
|
||||
|
||||
#### Card info substitution
|
||||
|
||||
This substitution retrieves information about a specific ALSA card by card number
|
||||
or card ID and returns the requested field value.
|
||||
|
||||
Usage examples:
|
||||
|
||||
~~~{.html}
|
||||
${info-card:card=0,field=name}
|
||||
${info-card:card=acp,field=driver}
|
||||
${info-card:card=PCH,field=longname}
|
||||
${info-card:card=$MyCard,field=$MyField}
|
||||
~~~
|
||||
|
||||
Arguments:
|
||||
|
||||
Argument | Description
|
||||
---------------------|--------------------------------------------------
|
||||
card | card number (integer), card ID (string), or variable name ($var)
|
||||
field | field to retrieve (number, id, driver, name, longname, mixername, components) or variable name ($var)
|
||||
|
||||
The **card** parameter can be either a card number (e.g., 0, 1, 2), a card ID string (e.g., "PCH", "acp", "Intel"),
|
||||
or a variable name prefixed with $ (e.g., $CardId).
|
||||
|
||||
The **field** parameter specifies which card information to return or can be a variable name prefixed with $ (e.g., $FieldName):
|
||||
- **number**: Card number (integer as string)
|
||||
- **id**: Card identifier
|
||||
- **driver**: Card driver name
|
||||
- **name**: Card short name
|
||||
- **longname**: Card long name
|
||||
- **mixername**: Mixer name
|
||||
- **components**: Card components
|
||||
|
||||
|
||||
### Variable defines
|
||||
|
|
@ -702,19 +738,135 @@ Define {
|
|||
}
|
||||
~~~
|
||||
|
||||
The *DefineRegex* allows substring extraction like:
|
||||
The *DefineRegex* allows substring extraction using regular expressions (POSIX extended regex).
|
||||
It can match patterns in strings and extract matched substrings into UCM variables.
|
||||
|
||||
#### DefineRegex Structure
|
||||
|
||||
~~~{.html}
|
||||
DefineRegex.rval {
|
||||
Regex "(hello)|(regex)"
|
||||
String "hello, it's my regex"
|
||||
DefineRegex.name {
|
||||
String "text to match against"
|
||||
Regex "regex_pattern"
|
||||
Flags "e"
|
||||
Scheme "first"
|
||||
}
|
||||
~~~
|
||||
|
||||
The result will be stored to variables *rval1* as *hello* and *rval2* as *regex* (every matched
|
||||
substrings are stored to a separate variable with the sequence number postfix.
|
||||
Field | Description
|
||||
---------------------|---------------------
|
||||
String | The input string to match the regex pattern against
|
||||
Regex | POSIX extended regular expression pattern
|
||||
Flags | Optional regex flags (see below)
|
||||
Scheme | Matching scheme: "first" (default) or "all" [**Syntax 9**]
|
||||
|
||||
Variables can be substituted using the `${var:rval1}` reference for example.
|
||||
#### Regex Flags
|
||||
|
||||
The Flags field is optional and accepts the following characters:
|
||||
|
||||
Flag | Description
|
||||
-------|---------------------
|
||||
e | Extended POSIX regex (REG_EXTENDED) - default recommended
|
||||
i | Case-insensitive matching (REG_ICASE)
|
||||
s | Report only success/fail (REG_NOSUB)
|
||||
n | Newline-sensitive matching (REG_NEWLINE)
|
||||
|
||||
Multiple flags can be combined, e.g., "ei" for extended and case-insensitive.
|
||||
|
||||
#### Matching Schemes
|
||||
|
||||
**Scheme "first"** (default): Matches the pattern once and extracts capture groups
|
||||
|
||||
The variables created are:
|
||||
- `name` - the full matched string
|
||||
- `name1` - first capture group (parentheses in regex)
|
||||
- `name2` - second capture group
|
||||
- `nameN` - Nth capture group
|
||||
|
||||
Example with "first" scheme:
|
||||
|
||||
~~~{.html}
|
||||
DefineRegex.hwdev {
|
||||
String "hw:2,0"
|
||||
Regex "hw:([0-9]+),([0-9]+)"
|
||||
Flags "e"
|
||||
Scheme "first"
|
||||
}
|
||||
~~~
|
||||
|
||||
This creates variables:
|
||||
- `hwdev` = "hw:2,0" (full match)
|
||||
- `hwdev1` = "2" (first capture group - card number)
|
||||
- `hwdev2` = "0" (second capture group - device number)
|
||||
|
||||
**Scheme "all"** [**Syntax 9**]: Matches the pattern multiple times and extracts all matches
|
||||
|
||||
The variables created are:
|
||||
- `nameN` - Nth full match (N starts at 1)
|
||||
- `nameN_1` - Nth match, first capture group
|
||||
- `nameN_2` - Nth match, second capture group
|
||||
- `nameN_M` - Nth match, Mth capture group
|
||||
|
||||
Example with "all" scheme:
|
||||
|
||||
~~~{.html}
|
||||
DefineRegex.devices {
|
||||
String "device1 device2 device3"
|
||||
Regex "device([0-9]+)"
|
||||
Flags "e"
|
||||
Scheme "all"
|
||||
}
|
||||
~~~
|
||||
|
||||
This creates variables:
|
||||
- `devices1` = "device1" (first full match)
|
||||
- `devices1_1` = "1" (first match, capture group 1)
|
||||
- `devices2` = "device2" (second full match)
|
||||
- `devices2_1` = "2" (second match, capture group 1)
|
||||
- `devices3` = "device3" (third full match)
|
||||
- `devices3_1` = "3" (third match, capture group 1)
|
||||
|
||||
#### Practical Examples
|
||||
|
||||
Extract USB device vendor and product IDs:
|
||||
|
||||
~~~{.html}
|
||||
DefineRegex.usbids {
|
||||
String "${sys:bus/usb/devices/1-1/uevent}"
|
||||
Regex "PRODUCT=([0-9a-f]+)/([0-9a-f]+)"
|
||||
Flags "e"
|
||||
Scheme "first"
|
||||
}
|
||||
# Creates: usbids (full match), usbids1 (vendor), usbids2 (product)
|
||||
~~~
|
||||
|
||||
Parse multiple key=value pairs:
|
||||
|
||||
~~~{.html}
|
||||
DefineRegex.params {
|
||||
String "rate=48000,channels=2,format=S16_LE"
|
||||
Regex "([a-z]+)=([^,]+)"
|
||||
Flags "e"
|
||||
Scheme "all"
|
||||
}
|
||||
# Creates: params1="rate=48000", params1_1="rate", params1_2="48000"
|
||||
# params2="channels=2", params2_1="channels", params2_2="2"
|
||||
# params3="format=S16_LE", params3_1="format", params3_2="S16_LE"
|
||||
~~~
|
||||
|
||||
Extract text components:
|
||||
|
||||
~~~{.html}
|
||||
DefineRegex.model {
|
||||
String "USB Audio Device Model XYZ123"
|
||||
Regex "([A-Z]+).*Model ([A-Z0-9]+)"
|
||||
Flags "e"
|
||||
Scheme "first"
|
||||
}
|
||||
# Creates: model (full match), model1="USB", model2="XYZ123"
|
||||
~~~
|
||||
|
||||
Variables can be substituted using `${var:name}` reference. For example, to use the extracted
|
||||
card number: `PlaybackPCM "hw:${var:hwdev1},0"`
|
||||
|
||||
### Macros
|
||||
|
||||
|
|
@ -878,6 +1030,32 @@ If.fmic {
|
|||
}
|
||||
~~~
|
||||
|
||||
#### Integer comparison (Type Integer)
|
||||
|
||||
Field | Description
|
||||
---------------------|-----------------------
|
||||
Operation | comparison operator (==, !=, <, >, <=, >=)
|
||||
Value1 | first integer value (string converted to long long)
|
||||
Value2 | second integer value (string converted to long long)
|
||||
|
||||
Note: Integer condition is supported in *Syntax* version *9*+.
|
||||
|
||||
Example:
|
||||
|
||||
~~~{.html}
|
||||
If.check_channels {
|
||||
Condition {
|
||||
Type Integer
|
||||
Operation ">"
|
||||
Value1 "${var:channels}"
|
||||
Value2 "2"
|
||||
}
|
||||
True {
|
||||
...
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
### Variants
|
||||
|
||||
To avoid duplication of the many configuration files for the cases with
|
||||
|
|
@ -986,6 +1164,136 @@ SectionDevice."HDMI:LowRate" {
|
|||
|
||||
This creates two devices: **HDMI:LowRate** (48kHz) and **HDMI:HighRate** (192kHz).
|
||||
|
||||
### Repetitive Pattern Substitution
|
||||
|
||||
Starting with **Syntax 9**, the UCM configuration supports the **Repeat** block for generating
|
||||
repetitive configuration patterns. This feature allows you to apply a configuration block multiple
|
||||
times with different variable values, reducing duplication in configuration files.
|
||||
|
||||
The **Repeat** block contains two main components:
|
||||
|
||||
1. **Pattern**: Defines the iteration pattern (how many times to repeat and what values to use)
|
||||
2. **Apply**: The configuration block to be applied on each iteration
|
||||
|
||||
#### Pattern Types
|
||||
|
||||
The **Pattern** block supports two types: **Integer** and **Array**.
|
||||
|
||||
**Integer Pattern**: Iterates over a range of integer values
|
||||
|
||||
~~~{.html}
|
||||
Repeat.MyRepeat {
|
||||
Pattern {
|
||||
Variable 'ChannelNum'
|
||||
Type Integer
|
||||
First 0
|
||||
Last 15
|
||||
Step 2
|
||||
}
|
||||
Apply {
|
||||
... configuration using ${var:ChannelNum} ...
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
Fields for Integer pattern:
|
||||
- **Variable**: Name of the variable to substitute (without ${var:} prefix)
|
||||
- **Type**: Must be "Integer"
|
||||
- **First**: Starting value (integer)
|
||||
- **Last**: Ending value (integer)
|
||||
- **Step**: Increment value (integer, default 1)
|
||||
|
||||
The iteration supports reverse order automatically when First is greater than Last.
|
||||
|
||||
**Array Pattern**: Iterates over a list of string values
|
||||
|
||||
~~~{.html}
|
||||
Repeat.DeviceList {
|
||||
Pattern {
|
||||
Variable 'DevName'
|
||||
Type Array
|
||||
Array [
|
||||
"Speaker"
|
||||
"Headphones"
|
||||
"HDMI"
|
||||
]
|
||||
}
|
||||
Apply {
|
||||
... configuration using ${var:DevName} ...
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
Fields for Array pattern:
|
||||
- **Variable**: Name of the variable to substitute (without ${var:} prefix)
|
||||
- **Type**: Must be "Array"
|
||||
- **Array**: A compound node containing string values to iterate over
|
||||
|
||||
**String Pattern**: Pattern can also be specified as a string that will be parsed as a
|
||||
configuration block. This allows for dynamic pattern generation.
|
||||
|
||||
~~~{.html}
|
||||
Repeat.Dynamic {
|
||||
Pattern "
|
||||
Variable 'Index'
|
||||
Type Integer
|
||||
First 1
|
||||
Last 4
|
||||
"
|
||||
Apply {
|
||||
... configuration using ${var:Index} ...
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
#### Complete Example
|
||||
|
||||
Example using Integer pattern to create multiple similar control settings:
|
||||
|
||||
~~~{.html}
|
||||
EnableSequence [
|
||||
Repeat.VolumeInit {
|
||||
Pattern {
|
||||
Variable 'ch'
|
||||
Type Integer
|
||||
First 0
|
||||
Last 7
|
||||
}
|
||||
Apply {
|
||||
cset "name='PCM Channel ${var:ch} Volume' 100%"
|
||||
}
|
||||
}
|
||||
]
|
||||
~~~
|
||||
|
||||
This generates 8 cset commands for channels 0 through 7.
|
||||
|
||||
Example using Array pattern for different device configurations:
|
||||
|
||||
~~~{.html}
|
||||
Repeat.Devices {
|
||||
Pattern {
|
||||
Variable 'output'
|
||||
Type Array
|
||||
Array [
|
||||
"Speaker"
|
||||
"Headphones"
|
||||
"LineOut"
|
||||
]
|
||||
}
|
||||
Apply {
|
||||
SectionDevice."${var:output}" {
|
||||
Comment "${var:output} Output"
|
||||
EnableSequence [
|
||||
cset "name='${var:output} Switch' on"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
This creates three SectionDevice blocks for Speaker, Headphones, and LineOut.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -90,12 +90,9 @@ static int include_eval_one(snd_use_case_mgr_t *uc_mgr,
|
|||
err = uc_mgr_get_substituted_value(uc_mgr, &s, file);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (opt_bool && access(s, R_OK) != 0) {
|
||||
snd_trace(UCM, "optional file '%s' not found", s);
|
||||
err = uc_mgr_config_load_file(uc_mgr, s, result, opt_bool);
|
||||
if (opt_bool && (err == -ENOENT || err == -EACCES))
|
||||
err = 0;
|
||||
} else {
|
||||
err = uc_mgr_config_load_file(uc_mgr, s, result);
|
||||
}
|
||||
free(s);
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
#include <stdbool.h>
|
||||
#include "use-case.h"
|
||||
|
||||
#define SYNTAX_VERSION_MAX 8
|
||||
#define SYNTAX_VERSION_MAX 9
|
||||
|
||||
#define MAX_CARD_SHORT_NAME 32
|
||||
#define MAX_CARD_LONG_NAME 80
|
||||
|
|
@ -300,9 +300,9 @@ void uc_mgr_stdout(const char *fmt, ...);
|
|||
|
||||
const char *uc_mgr_sysfs_root(void);
|
||||
const char *uc_mgr_config_dir(int format);
|
||||
int uc_mgr_config_load_into(int format, const char *file, snd_config_t *cfg);
|
||||
int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg);
|
||||
int uc_mgr_config_load_file(snd_use_case_mgr_t *uc_mgr, const char *file, snd_config_t **cfg);
|
||||
int uc_mgr_config_load_into(int format, const char *file, snd_config_t *cfg, bool optional);
|
||||
int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg, bool optional);
|
||||
int uc_mgr_config_load_file(snd_use_case_mgr_t *uc_mgr, const char *file, snd_config_t **cfg, bool optional);
|
||||
int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr);
|
||||
int uc_mgr_scan_master_configs(const char **_list[]);
|
||||
|
||||
|
|
@ -352,7 +352,8 @@ int uc_mgr_add_value(struct list_head *base, const char *key, char *val);
|
|||
int uc_mgr_check_value(struct list_head *value_list, const char *identifier);
|
||||
|
||||
const char *uc_mgr_get_variable(snd_use_case_mgr_t *uc_mgr,
|
||||
const char *name);
|
||||
const char *name,
|
||||
bool show_err);
|
||||
|
||||
int uc_mgr_set_variable(snd_use_case_mgr_t *uc_mgr,
|
||||
const char *name,
|
||||
|
|
@ -384,6 +385,9 @@ int uc_mgr_evaluate_condition(snd_use_case_mgr_t *uc_mgr,
|
|||
snd_config_t *parent,
|
||||
snd_config_t *cond);
|
||||
|
||||
int uc_mgr_evaluate_repeat(snd_use_case_mgr_t *uc_mgr,
|
||||
snd_config_t *cfg);
|
||||
|
||||
int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr,
|
||||
const char *name,
|
||||
snd_config_t *eval);
|
||||
|
|
|
|||
|
|
@ -54,47 +54,78 @@ static char *extract_substring(const char *data, regmatch_t *match)
|
|||
}
|
||||
|
||||
static int set_variables(snd_use_case_mgr_t *uc_mgr, const char *data,
|
||||
unsigned int match_size, regmatch_t *match,
|
||||
const char *name)
|
||||
regex_t *re, const char *name, int scheme_all)
|
||||
{
|
||||
size_t name2_len = strlen(name) + 16;
|
||||
char *name2 = alloca(name2_len);
|
||||
size_t name_len = strlen(name) + 32;
|
||||
char *var_name = alloca(name_len);
|
||||
regmatch_t match[20];
|
||||
char *s;
|
||||
unsigned int match_idx = 1;
|
||||
unsigned int i;
|
||||
int err;
|
||||
const char *pos;
|
||||
|
||||
if (match[0].rm_so < 0 || match[0].rm_eo < 0)
|
||||
return 0;
|
||||
s = extract_substring(data, &match[0]);
|
||||
if (s == NULL)
|
||||
return -ENOMEM;
|
||||
err = uc_mgr_set_variable(uc_mgr, name, s);
|
||||
free(s);
|
||||
if (err < 0)
|
||||
return err;
|
||||
for (i = 1; i < match_size; i++) {
|
||||
if (match[i].rm_so < 0 || match[i].rm_eo < 0)
|
||||
return 0;
|
||||
s = extract_substring(data, &match[i]);
|
||||
pos = data;
|
||||
while (1) {
|
||||
err = regexec(re, pos, ARRAY_SIZE(match), match, 0);
|
||||
if (err == REG_NOMATCH)
|
||||
break;
|
||||
if (err != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (match[0].rm_so < 0 || match[0].rm_eo < 0)
|
||||
break;
|
||||
|
||||
s = extract_substring(pos, &match[0]);
|
||||
if (s == NULL)
|
||||
return -ENOMEM;
|
||||
snprintf(name2, name2_len, "%s%u", name, i);
|
||||
err = uc_mgr_set_variable(uc_mgr, name2, s);
|
||||
if (scheme_all) {
|
||||
snprintf(var_name, name_len, "%s%u", name, match_idx);
|
||||
err = uc_mgr_set_variable(uc_mgr, var_name, s);
|
||||
} else {
|
||||
err = uc_mgr_set_variable(uc_mgr, name, s);
|
||||
}
|
||||
free(s);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 1; i < ARRAY_SIZE(match); i++) {
|
||||
if (match[i].rm_so < 0 || match[i].rm_eo < 0)
|
||||
break;
|
||||
s = extract_substring(pos, &match[i]);
|
||||
if (s == NULL)
|
||||
return -ENOMEM;
|
||||
if (scheme_all)
|
||||
snprintf(var_name, name_len, "%s%u_%u", name, match_idx, i);
|
||||
else
|
||||
snprintf(var_name, name_len, "%s%u", name, i);
|
||||
err = uc_mgr_set_variable(uc_mgr, var_name, s);
|
||||
free(s);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!scheme_all)
|
||||
break;
|
||||
|
||||
pos += match[0].rm_eo;
|
||||
match_idx++;
|
||||
|
||||
if (*pos == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr, const char *name,
|
||||
snd_config_t *eval)
|
||||
{
|
||||
const char *string, *regex_string, *flags_string;
|
||||
const char *string, *regex_string, *flags_string, *scheme_string;
|
||||
char *s;
|
||||
regex_t re;
|
||||
int options = 0;
|
||||
regmatch_t match[20];
|
||||
int use_scheme_all = 0;
|
||||
int err;
|
||||
|
||||
if (uc_mgr->conf_format < 3) {
|
||||
|
|
@ -119,6 +150,27 @@ int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr, const char *name,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = get_string(eval, "Scheme", &scheme_string);
|
||||
if (err == -ENOENT) {
|
||||
use_scheme_all = 0;
|
||||
} else if (err < 0) {
|
||||
snd_error(UCM, "DefineRegex error (Scheme string)");
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (strcmp(scheme_string, "first") == 0) {
|
||||
use_scheme_all = 0;
|
||||
} else if (strcmp(scheme_string, "all") == 0) {
|
||||
if (uc_mgr->conf_format < 9) {
|
||||
snd_error(UCM, "DefineRegex 'all' scheme is supported in v9+ syntax");
|
||||
return -EINVAL;
|
||||
}
|
||||
use_scheme_all = 1;
|
||||
} else {
|
||||
snd_error(UCM, "DefineRegex error (unknown scheme '%s')", scheme_string);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
err = get_string(eval, "Flags", &flags_string);
|
||||
if (err == -ENOENT) {
|
||||
options = REG_EXTENDED;
|
||||
|
|
@ -164,13 +216,8 @@ int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr, const char *name,
|
|||
regfree(&re);
|
||||
return err;
|
||||
}
|
||||
err = regexec(&re, s, ARRAY_SIZE(match), match, 0);
|
||||
if (err < 0)
|
||||
err = -errno;
|
||||
else if (err == REG_NOMATCH)
|
||||
err = 0;
|
||||
else
|
||||
err = set_variables(uc_mgr, s, ARRAY_SIZE(match), match, name);
|
||||
|
||||
err = set_variables(uc_mgr, s, &re, name, use_scheme_all);
|
||||
free(s);
|
||||
regfree(&re);
|
||||
return err;
|
||||
|
|
|
|||
401
src/ucm/ucm_repeat.c
Normal file
401
src/ucm/ucm_repeat.c
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
/*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Copyright (C) 2026 Red Hat Inc.
|
||||
* Authors: Jaroslav Kysela <perex@perex.cz>
|
||||
*/
|
||||
|
||||
#include "ucm_local.h"
|
||||
|
||||
/*
|
||||
* get_string helper
|
||||
*/
|
||||
static int get_string(snd_config_t *compound, const char *key, const char **str)
|
||||
{
|
||||
snd_config_t *node;
|
||||
int err;
|
||||
|
||||
err = snd_config_search(compound, key, &node);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return snd_config_get_string(node, str);
|
||||
}
|
||||
|
||||
/*
|
||||
* get_integer helper
|
||||
*/
|
||||
static int get_integer(snd_config_t *compound, const char *key, long long *val)
|
||||
{
|
||||
snd_config_type_t t;
|
||||
snd_config_t *node;
|
||||
const char *str;
|
||||
int err;
|
||||
|
||||
err = snd_config_search(compound, key, &node);
|
||||
if (err < 0)
|
||||
return err;
|
||||
t = snd_config_get_type(node);
|
||||
if (t == SND_CONFIG_TYPE_INTEGER) {
|
||||
long i;
|
||||
err = snd_config_get_integer(node, &i);
|
||||
if (err >= 0)
|
||||
*val = i;
|
||||
} else if (t == SND_CONFIG_TYPE_INTEGER64) {
|
||||
err = snd_config_get_integer64(node, val);
|
||||
} else {
|
||||
err = snd_config_get_string(node, &str);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = safe_strtoll(str, val);
|
||||
}
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Repeat pattern iterator
|
||||
*/
|
||||
struct repeat_iterator {
|
||||
const char *var_name;
|
||||
|
||||
union {
|
||||
struct {
|
||||
long long current;
|
||||
long long last;
|
||||
long long step;
|
||||
int iteration;
|
||||
char value_buf[32];
|
||||
} integer;
|
||||
|
||||
struct {
|
||||
snd_config_iterator_t pos;
|
||||
snd_config_iterator_t end;
|
||||
snd_config_t *array;
|
||||
char *value_str;
|
||||
} array;
|
||||
} u;
|
||||
|
||||
int (*init)(struct repeat_iterator *it, snd_config_t *pattern);
|
||||
int (*next)(struct repeat_iterator *it, const char **value);
|
||||
void (*done)(struct repeat_iterator *it);
|
||||
};
|
||||
|
||||
/*
|
||||
* Integer pattern iterator - initialization
|
||||
*/
|
||||
static int repeat_integer_init(struct repeat_iterator *it, snd_config_t *pattern)
|
||||
{
|
||||
long long first;
|
||||
int err;
|
||||
|
||||
err = get_integer(pattern, "First", &first);
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "Repeat.Pattern.First is required for Integer type");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = get_integer(pattern, "Last", &it->u.integer.last);
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "Repeat.Pattern.Last is required for Integer type");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = get_integer(pattern, "Step", &it->u.integer.step);
|
||||
if (err == -ENOENT) {
|
||||
it->u.integer.step = 1;
|
||||
} else if (err < 0) {
|
||||
snd_error(UCM, "Repeat.Pattern.Step parse error");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (it->u.integer.step == 0) {
|
||||
snd_error(UCM, "Repeat.Pattern.Step cannot be zero");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
it->u.integer.current = first;
|
||||
it->u.integer.iteration = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Integer pattern iterator - get next value
|
||||
* Returns: 1 if value available, 0 if end of iteration, negative on error
|
||||
*/
|
||||
static int repeat_integer_next(struct repeat_iterator *it, const char **value)
|
||||
{
|
||||
const int max_iterations = 10000;
|
||||
int has_value;
|
||||
|
||||
if (it->u.integer.iteration++ > max_iterations) {
|
||||
snd_error(UCM, "Repeat iteration limit exceeded");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (it->u.integer.step > 0)
|
||||
has_value = (it->u.integer.current <= it->u.integer.last);
|
||||
else
|
||||
has_value = (it->u.integer.current >= it->u.integer.last);
|
||||
|
||||
if (!has_value)
|
||||
return 0;
|
||||
|
||||
snprintf(it->u.integer.value_buf, sizeof(it->u.integer.value_buf), "%lld", it->u.integer.current);
|
||||
*value = it->u.integer.value_buf;
|
||||
|
||||
it->u.integer.current += it->u.integer.step;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Array pattern iterator - initialization
|
||||
*/
|
||||
static int repeat_array_init(struct repeat_iterator *it, snd_config_t *pattern)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = snd_config_search(pattern, "Array", &it->u.array.array);
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "Repeat.Pattern.Array is required for Array type");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (snd_config_get_type(it->u.array.array) != SND_CONFIG_TYPE_COMPOUND) {
|
||||
snd_error(UCM, "Repeat.Pattern.Array must be a compound");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
it->u.array.pos = snd_config_iterator_first(it->u.array.array);
|
||||
it->u.array.end = snd_config_iterator_end(it->u.array.array);
|
||||
it->u.array.value_str = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Array pattern iterator - get next value
|
||||
* Returns: 1 if value available, 0 if end of iteration, negative on error
|
||||
*/
|
||||
static int repeat_array_next(struct repeat_iterator *it, const char **value)
|
||||
{
|
||||
snd_config_t *n;
|
||||
int err;
|
||||
|
||||
/* Free previous value string */
|
||||
free(it->u.array.value_str);
|
||||
it->u.array.value_str = NULL;
|
||||
|
||||
if (it->u.array.pos == it->u.array.end)
|
||||
return 0;
|
||||
|
||||
n = snd_config_iterator_entry(it->u.array.pos);
|
||||
it->u.array.pos = snd_config_iterator_next(it->u.array.pos);
|
||||
|
||||
err = snd_config_get_ascii(n, &it->u.array.value_str);
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "Repeat.Pattern.Array element conversion error");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*value = it->u.array.value_str;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Array pattern iterator - cleanup
|
||||
*/
|
||||
static void repeat_array_done(struct repeat_iterator *it)
|
||||
{
|
||||
free(it->u.array.value_str);
|
||||
it->u.array.value_str = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluate repeat pattern using iterator
|
||||
*/
|
||||
static int evaluate_repeat_pattern(snd_use_case_mgr_t *uc_mgr,
|
||||
snd_config_t *cfg,
|
||||
snd_config_t *pattern,
|
||||
snd_config_t *apply,
|
||||
struct repeat_iterator *it)
|
||||
{
|
||||
snd_config_t *apply_copy;
|
||||
const char *value;
|
||||
int err, ret;
|
||||
|
||||
err = it->init(it, pattern);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
while ((ret = it->next(it, &value)) > 0) {
|
||||
err = uc_mgr_set_variable(uc_mgr, it->var_name, value);
|
||||
if (err < 0)
|
||||
goto __error;
|
||||
|
||||
err = snd_config_copy(&apply_copy, apply);
|
||||
if (err < 0)
|
||||
goto __var_error;
|
||||
|
||||
err = uc_mgr_evaluate_inplace(uc_mgr, apply_copy);
|
||||
if (err < 0)
|
||||
goto __copy_error;
|
||||
|
||||
err = uc_mgr_config_tree_merge(uc_mgr, cfg, apply_copy, NULL, NULL);
|
||||
snd_config_delete(apply_copy);
|
||||
if (err < 0)
|
||||
goto __var_error;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
err = ret;
|
||||
goto __var_error;
|
||||
}
|
||||
|
||||
uc_mgr_delete_variable(uc_mgr, it->var_name);
|
||||
|
||||
if (it->done)
|
||||
it->done(it);
|
||||
|
||||
return 0;
|
||||
|
||||
__copy_error:
|
||||
snd_config_delete(apply_copy);
|
||||
__var_error:
|
||||
uc_mgr_delete_variable(uc_mgr, it->var_name);
|
||||
__error:
|
||||
if (it->done)
|
||||
it->done(it);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluate repeat (in-place)
|
||||
*/
|
||||
int uc_mgr_evaluate_repeat(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
|
||||
{
|
||||
snd_config_iterator_t i, next;
|
||||
snd_config_t *repeat_blocks, *n, *pattern = NULL, *pattern_cfg = NULL;
|
||||
const char *id;
|
||||
int err;
|
||||
|
||||
err = snd_config_search(cfg, "Repeat", &repeat_blocks);
|
||||
if (err == -ENOENT)
|
||||
return 1;
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (uc_mgr->conf_format < 9) {
|
||||
snd_error(UCM, "Repeat is supported in v9+ syntax");
|
||||
err = -EINVAL;
|
||||
goto __error;
|
||||
}
|
||||
|
||||
if (snd_config_get_type(repeat_blocks) != SND_CONFIG_TYPE_COMPOUND) {
|
||||
snd_error(UCM, "Repeat must be a compound");
|
||||
err = -EINVAL;
|
||||
goto __error;
|
||||
}
|
||||
|
||||
snd_config_for_each(i, next, repeat_blocks) {
|
||||
snd_config_t *apply;
|
||||
struct repeat_iterator it;
|
||||
const char *var_name, *type_str;
|
||||
|
||||
n = snd_config_iterator_entry(i);
|
||||
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
|
||||
err = snd_config_search(n, "Pattern", &pattern);
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "Repeat.%s.Pattern is required", id);
|
||||
goto __error;
|
||||
}
|
||||
|
||||
if (snd_config_get_type(pattern) == SND_CONFIG_TYPE_STRING) {
|
||||
const char *pattern_str;
|
||||
char *pattern_subst = NULL;
|
||||
|
||||
err = snd_config_get_string(pattern, &pattern_str);
|
||||
if (err < 0)
|
||||
goto __error;
|
||||
|
||||
err = uc_mgr_get_substituted_value(uc_mgr, &pattern_subst, pattern_str);
|
||||
if (err < 0)
|
||||
goto __error;
|
||||
|
||||
err = snd_config_load_string(&pattern_cfg, pattern_subst, 0);
|
||||
free(pattern_subst);
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "Repeat.%s.Pattern string parse error", id);
|
||||
goto __error;
|
||||
}
|
||||
} else {
|
||||
pattern_cfg = pattern;
|
||||
}
|
||||
|
||||
err = get_string(pattern_cfg, "Variable", &var_name);
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "Repeat.%s.Pattern.Variable is required", id);
|
||||
goto __pattern_error;
|
||||
}
|
||||
|
||||
err = get_string(pattern_cfg, "Type", &type_str);
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "Repeat.%s.Pattern.Type is required", id);
|
||||
goto __pattern_error;
|
||||
}
|
||||
|
||||
err = snd_config_search(n, "Apply", &apply);
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "Repeat.%s.Apply is required", id);
|
||||
goto __pattern_error;
|
||||
}
|
||||
|
||||
memset(&it, 0, sizeof(it));
|
||||
it.var_name = var_name;
|
||||
|
||||
if (strcmp(type_str, "Integer") == 0) {
|
||||
it.init = repeat_integer_init;
|
||||
it.next = repeat_integer_next;
|
||||
it.done = NULL;
|
||||
} else if (strcmp(type_str, "Array") == 0) {
|
||||
it.init = repeat_array_init;
|
||||
it.next = repeat_array_next;
|
||||
it.done = repeat_array_done;
|
||||
} else {
|
||||
snd_error(UCM, "Repeat.%s.Pattern.Type must be 'Integer' or 'Array'", id);
|
||||
err = -EINVAL;
|
||||
goto __pattern_error;
|
||||
}
|
||||
|
||||
err = evaluate_repeat_pattern(uc_mgr, cfg, pattern_cfg, apply, &it);
|
||||
if (err < 0)
|
||||
goto __pattern_error;
|
||||
if (pattern_cfg != pattern) {
|
||||
snd_config_delete(pattern_cfg);
|
||||
pattern_cfg = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
err = 0;
|
||||
__pattern_error:
|
||||
if (pattern_cfg && pattern_cfg != pattern)
|
||||
snd_config_delete(pattern_cfg);
|
||||
__error:
|
||||
snd_config_delete(repeat_blocks);
|
||||
return err;
|
||||
}
|
||||
|
|
@ -204,6 +204,92 @@ static char *rval_card_id_by_name(snd_use_case_mgr_t *uc_mgr, const char *id)
|
|||
return strdup(snd_ctl_card_info_get_id(ctl_list->ctl_info));
|
||||
}
|
||||
|
||||
static char *rval_card_info(snd_use_case_mgr_t *uc_mgr, const char *query)
|
||||
{
|
||||
snd_config_t *config, *d;
|
||||
const char *card_str, *field_str, *tmp;
|
||||
struct ctl_list *ctl_list = NULL;
|
||||
snd_ctl_card_info_t *info;
|
||||
char *result = NULL;
|
||||
long card_num;
|
||||
int err;
|
||||
|
||||
if (uc_mgr->conf_format < 9) {
|
||||
snd_error(UCM, "info-card substitution is supported in v9+ syntax");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err = snd_config_load_string(&config, query, 0);
|
||||
if (err < 0) {
|
||||
snd_error(UCM, "info-card: invalid arguments '%s'", query);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (snd_config_search(config, "card", &d)) {
|
||||
snd_error(UCM, "info-card: 'card' parameter is required");
|
||||
goto __error;
|
||||
}
|
||||
if (snd_config_get_string(d, &card_str))
|
||||
goto __error;
|
||||
|
||||
if (card_str[0] == '$') {
|
||||
tmp = card_str + 1;
|
||||
card_str = uc_mgr_get_variable(uc_mgr, tmp, true);
|
||||
if (card_str == NULL)
|
||||
goto __error;
|
||||
}
|
||||
|
||||
if (snd_config_search(config, "field", &d)) {
|
||||
snd_error(UCM, "info-card: 'field' parameter is required");
|
||||
goto __error;
|
||||
}
|
||||
if (snd_config_get_string(d, &field_str))
|
||||
goto __error;
|
||||
|
||||
if (field_str[0] == '$') {
|
||||
tmp = field_str + 1;
|
||||
field_str = uc_mgr_get_variable(uc_mgr, tmp, true);
|
||||
if (field_str == NULL)
|
||||
goto __error;
|
||||
}
|
||||
|
||||
if (safe_strtol(card_str, &card_num) == 0)
|
||||
ctl_list = uc_mgr_get_ctl_by_card(uc_mgr, (int)card_num);
|
||||
if (ctl_list == NULL)
|
||||
ctl_list = get_ctl_list_by_name(uc_mgr, card_str);
|
||||
if (ctl_list == NULL) {
|
||||
snd_error(UCM, "info-card: card '%s' not found", card_str);
|
||||
goto __error;
|
||||
}
|
||||
|
||||
info = ctl_list->ctl_info;
|
||||
|
||||
if (strcasecmp(field_str, "number") == 0) {
|
||||
char num[16];
|
||||
snprintf(num, sizeof(num), "%d", snd_ctl_card_info_get_card(info));
|
||||
result = strdup(num);
|
||||
} else if (strcasecmp(field_str, "id") == 0) {
|
||||
result = strdup(snd_ctl_card_info_get_id(info));
|
||||
} else if (strcasecmp(field_str, "driver") == 0) {
|
||||
result = strdup(snd_ctl_card_info_get_driver(info));
|
||||
} else if (strcasecmp(field_str, "name") == 0) {
|
||||
result = strdup(snd_ctl_card_info_get_name(info));
|
||||
} else if (strcasecmp(field_str, "longname") == 0) {
|
||||
result = strdup(snd_ctl_card_info_get_longname(info));
|
||||
} else if (strcasecmp(field_str, "mixername") == 0) {
|
||||
result = strdup(snd_ctl_card_info_get_mixername(info));
|
||||
} else if (strcasecmp(field_str, "components") == 0) {
|
||||
result = strdup(snd_ctl_card_info_get_components(info));
|
||||
} else {
|
||||
snd_error(UCM, "info-card: unknown field '%s'", field_str);
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
__error:
|
||||
snd_config_delete(config);
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef DOC_HIDDEN
|
||||
typedef struct lookup_iterate *(*lookup_iter_fcn_t)
|
||||
(snd_use_case_mgr_t *uc_mgr, struct lookup_iterate *iter);
|
||||
|
|
@ -235,7 +321,7 @@ static char *rval_lookup_main(snd_use_case_mgr_t *uc_mgr,
|
|||
snd_config_t *config, *d;
|
||||
struct lookup_fcn *fcn;
|
||||
struct lookup_iterate *curr;
|
||||
const char *s;
|
||||
const char *s, *tmp;
|
||||
char *result;
|
||||
regmatch_t match[1];
|
||||
regex_t re;
|
||||
|
|
@ -259,6 +345,12 @@ static char *rval_lookup_main(snd_use_case_mgr_t *uc_mgr,
|
|||
}
|
||||
if (snd_config_get_string(d, &s))
|
||||
goto null;
|
||||
if (s[0] == '$' && uc_mgr->conf_format >= 9) {
|
||||
tmp = s + 1;
|
||||
s = uc_mgr_get_variable(uc_mgr, tmp, true);
|
||||
if (s == NULL)
|
||||
goto null;
|
||||
}
|
||||
for (fcn = iter->fcns ; fcn; fcn++) {
|
||||
if (strcasecmp(fcn->name, s) == 0) {
|
||||
iter->fcn = fcn->fcn;
|
||||
|
|
@ -275,6 +367,12 @@ static char *rval_lookup_main(snd_use_case_mgr_t *uc_mgr,
|
|||
}
|
||||
if (snd_config_get_string(d, &s))
|
||||
goto null;
|
||||
if (s[0] == '$' && uc_mgr->conf_format >= 9) {
|
||||
tmp = s + 1;
|
||||
s = uc_mgr_get_variable(uc_mgr, tmp, true);
|
||||
if (s == NULL)
|
||||
goto null;
|
||||
}
|
||||
err = regcomp(&re, s, REG_EXTENDED | REG_ICASE);
|
||||
if (err) {
|
||||
snd_error(UCM, "Regex '%s' compilation failed (code %d)", s, err);
|
||||
|
|
@ -410,7 +508,8 @@ static char *rval_pcm_lookup_return(struct lookup_iterate *iter,
|
|||
return strdup(num);
|
||||
}
|
||||
|
||||
static int rval_pcm_lookup_init(struct lookup_iterate *iter,
|
||||
static int rval_pcm_lookup_init(snd_use_case_mgr_t *uc_mgr,
|
||||
struct lookup_iterate *iter,
|
||||
snd_config_t *config)
|
||||
{
|
||||
static struct lookup_fcn pcm_fcns[] = {
|
||||
|
|
@ -420,12 +519,18 @@ static int rval_pcm_lookup_init(struct lookup_iterate *iter,
|
|||
{ 0 },
|
||||
};
|
||||
snd_config_t *d;
|
||||
const char *s;
|
||||
const char *s, *tmp;
|
||||
snd_pcm_info_t *pcminfo;
|
||||
snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
|
||||
|
||||
if (snd_config_search(config, "stream", &d) == 0 &&
|
||||
snd_config_get_string(d, &s) == 0) {
|
||||
if (s[0] == '$' && uc_mgr->conf_format >= 9) {
|
||||
tmp = s + 1;
|
||||
s = uc_mgr_get_variable(uc_mgr, tmp, true);
|
||||
if (s == NULL)
|
||||
return -EINVAL;
|
||||
}
|
||||
if (strcasecmp(s, "playback") == 0)
|
||||
stream = SND_PCM_STREAM_PLAYBACK;
|
||||
else if (strcasecmp(s, "capture") == 0)
|
||||
|
|
@ -454,13 +559,14 @@ static int rval_device_lookup_init(snd_use_case_mgr_t *uc_mgr,
|
|||
{
|
||||
static struct {
|
||||
const char *name;
|
||||
int (*init)(struct lookup_iterate *iter, snd_config_t *config);
|
||||
int (*init)(snd_use_case_mgr_t *uc_mgr, struct lookup_iterate *iter,
|
||||
snd_config_t *config);
|
||||
} *t, types[] = {
|
||||
{ .name = "pcm", .init = rval_pcm_lookup_init },
|
||||
{ 0 }
|
||||
};
|
||||
snd_config_t *d;
|
||||
const char *s;
|
||||
const char *s, *tmp;
|
||||
int err;
|
||||
|
||||
if (snd_config_search(config, "ctl", &d) || snd_config_get_string(d, &s)) {
|
||||
|
|
@ -480,9 +586,15 @@ static int rval_device_lookup_init(snd_use_case_mgr_t *uc_mgr,
|
|||
snd_error(UCM, "Missing device type!");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (s[0] == '$' && uc_mgr->conf_format >= 9) {
|
||||
tmp = s + 1;
|
||||
s = uc_mgr_get_variable(uc_mgr, tmp, true);
|
||||
if (s == NULL)
|
||||
return -EINVAL;
|
||||
}
|
||||
for (t = types; t->name; t++)
|
||||
if (strcasecmp(t->name, s) == 0)
|
||||
return t->init(iter, config);
|
||||
return t->init(uc_mgr, iter, config);
|
||||
snd_error(UCM, "Device type '%s' is invalid", s);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -726,7 +838,7 @@ static char *rval_var(snd_use_case_mgr_t *uc_mgr, const char *id)
|
|||
} else if (id[0] == '@') {
|
||||
ignore_not_found = true;
|
||||
}
|
||||
v = uc_mgr_get_variable(uc_mgr, id);
|
||||
v = uc_mgr_get_variable(uc_mgr, id, false);
|
||||
if (v == NULL && ignore_not_found)
|
||||
v = "";
|
||||
if (v)
|
||||
|
|
@ -742,7 +854,7 @@ static int rval_eval_var_cb(snd_config_t **dst, const char *s, void *private_dat
|
|||
snd_use_case_mgr_t *uc_mgr = private_data;
|
||||
const char *v;
|
||||
|
||||
v = uc_mgr_get_variable(uc_mgr, s);
|
||||
v = uc_mgr_get_variable(uc_mgr, s, false);
|
||||
if (v == NULL)
|
||||
return -ENOENT;
|
||||
return snd_config_imake_string(dst, NULL, v);
|
||||
|
|
@ -826,7 +938,7 @@ static int rval_evali(snd_use_case_mgr_t *uc_mgr, snd_config_t *node, const char
|
|||
*/
|
||||
static inline const char *strchr_with_escape(const char *str, char c)
|
||||
{
|
||||
char *s;
|
||||
const char *s;
|
||||
|
||||
while (1) {
|
||||
s = strchr(str, c);
|
||||
|
|
@ -913,6 +1025,7 @@ __std:
|
|||
MATCH_VARIABLE2(value, "${eval:", rval_eval, false);
|
||||
MATCH_VARIABLE2(value, "${find-card:", rval_card_lookup, false);
|
||||
MATCH_VARIABLE2(value, "${find-device:", rval_device_lookup, false);
|
||||
MATCH_VARIABLE2(value, "${info-card:", rval_card_info, false);
|
||||
MATCH_VARIABLE2(value, "${CardNumberByName:", rval_card_number_by_name, false);
|
||||
MATCH_VARIABLE2(value, "${CardIdByName:", rval_card_id_by_name, false);
|
||||
__merr:
|
||||
|
|
@ -939,7 +1052,7 @@ __match2:
|
|||
if (*v2 == '$' && uc_mgr->conf_format >= 3) {
|
||||
if (strncmp(value, "${eval:", 7) == 0)
|
||||
goto __direct_fcn2;
|
||||
tmp = uc_mgr_get_variable(uc_mgr, v2 + 1);
|
||||
tmp = uc_mgr_get_variable(uc_mgr, v2 + 1, false);
|
||||
if (tmp == NULL) {
|
||||
snd_error(UCM, "define '%s' is not reachable in this context!", v2 + 1);
|
||||
rval = NULL;
|
||||
|
|
|
|||
|
|
@ -357,7 +357,7 @@ const char *uc_mgr_config_dir(int format)
|
|||
return path;
|
||||
}
|
||||
|
||||
int uc_mgr_config_load_into(int format, const char *file, snd_config_t *top)
|
||||
int uc_mgr_config_load_into(int format, const char *file, snd_config_t *top, bool optional)
|
||||
{
|
||||
FILE *fp;
|
||||
snd_input_t *in;
|
||||
|
|
@ -369,7 +369,10 @@ int uc_mgr_config_load_into(int format, const char *file, snd_config_t *top)
|
|||
if (!fp) {
|
||||
err = -errno;
|
||||
__err_open:
|
||||
snd_error(UCM, "could not open configuration file %s", file);
|
||||
if (!optional || (err != -ENOENT && err != -EACCES))
|
||||
snd_error(UCM, "could not open configuration file %s", file);
|
||||
else
|
||||
snd_trace(UCM, "optional configuration file %s not found or readable", file);
|
||||
return err;
|
||||
}
|
||||
err = snd_input_stdio_attach(&in, fp, 1);
|
||||
|
|
@ -391,7 +394,7 @@ int uc_mgr_config_load_into(int format, const char *file, snd_config_t *top)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg)
|
||||
int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg, bool optional)
|
||||
{
|
||||
snd_config_t *top;
|
||||
int err;
|
||||
|
|
@ -399,7 +402,7 @@ int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg)
|
|||
err = snd_config_top(&top);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = uc_mgr_config_load_into(format, file, top);
|
||||
err = uc_mgr_config_load_into(format, file, top, optional);
|
||||
if (err < 0) {
|
||||
snd_config_delete(top);
|
||||
return err;
|
||||
|
|
@ -672,7 +675,7 @@ int uc_mgr_remove_device(struct use_case_verb *verb, const char *name)
|
|||
return found == 0 ? -ENODEV : 0;
|
||||
}
|
||||
|
||||
const char *uc_mgr_get_variable(snd_use_case_mgr_t *uc_mgr, const char *name)
|
||||
const char *uc_mgr_get_variable(snd_use_case_mgr_t *uc_mgr, const char *name, bool show_err)
|
||||
{
|
||||
struct list_head *pos;
|
||||
struct ucm_value *value;
|
||||
|
|
@ -682,6 +685,8 @@ const char *uc_mgr_get_variable(snd_use_case_mgr_t *uc_mgr, const char *name)
|
|||
if (strcmp(value->name, name) == 0)
|
||||
return value->data;
|
||||
}
|
||||
if (show_err)
|
||||
snd_error(UCM, "variable '%s' is not defined", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue