From 90f59671784a7e47b40485095cd66892d4840ed7 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 11 Dec 2021 10:45:43 -0800 Subject: [PATCH] topology: pre-processor: Move the call to expand variables Remove the call to snd_config_expand_custom() to expand the top-level input config. And replace it with calls to snd_config_evaluate_string() for each non-compound config while processing individual objects. This will allow retreving variable definitions from object attribute values and global definitions. Add a new field "current_obj_cfg" to hold the current object config being pre-processed. This will facilitate adding simple math expressions for computing attribute values for objects based on other attributes. For ex: we can set the expression for buffer size as follows: buffer_size "$[($in_channels * 48) * 4]" The buffer_size attribute value will be computed with the attribute value "in_channels" based on the expression above. So if $in_channels = 2, buffer_size will be evaluated to 384. Additionally this change also permits computing attribute values based on previously computed values. For example: buffer_size "$[($in_channels * 48) * 4]" dma_buffer_size "$[$buffer_size * 2]" dma_buffer_size will be computed as 768. Note that the order of definitions for buffer_size and dma_buffer_size matters because the evaluation for dma_buffer_size depends on the evaluation of buffer_size. In order to conform to this, the tplg_object_copy_and_add_param() is modified to add attribute configs from class config to an object using snd_config_before() instead of snd_config_add(). With this change, we no longer need to set the auto_attr_updater for buffer type widget objects. So remove it. Signed-off-by: Ranjani Sridharan Signed-off-by: Jaroslav Kysela --- alsaucm/go.sh | 8 ++- aplay/aplay.c | 38 +++++++++++ topology/pre-process-dapm.c | 109 ------------------------------ topology/pre-process-object.c | 120 ++++++++++++++++++++++++++++++++-- topology/pre-processor.c | 59 ----------------- topology/pre-processor.h | 2 - topology/topology.h | 1 + 7 files changed, 158 insertions(+), 179 deletions(-) diff --git a/alsaucm/go.sh b/alsaucm/go.sh index 1754fa2..8f1b93b 100755 --- a/alsaucm/go.sh +++ b/alsaucm/go.sh @@ -3,9 +3,13 @@ #GDB="gdb --args" #GDB="strace" #GDB="valgrind --leak-check=yes --show-reachable=yes" -GDB="perf stat" +#GDB="perf stat" +PROG=./alsaucm +PROG=/home/perex/git/pipewire/builddir/spa/plugins/alsa/spa-acp-tool +PROG="$HOME/git/pulseaudio/build/src/daemon/pulseaudio -n -F $HOME/git/pulseaudio/build/src/daemon/default.pa -p $HOME/git/pulseaudio/build/src/modules/" +#PROG=pulseaudio #ALSA_CONFIG_UCM="$HOME/alsa/alsa-ucm-conf/ucm" \ ALSA_CONFIG_UCM2="$HOME/alsa/alsa-ucm-conf/ucm2" \ LD_PRELOAD="$HOME/alsa/alsa-lib/src/.libs/libasound.so" \ -$GDB ./alsaucm "$@" +$GDB $PROG "$@" diff --git a/aplay/aplay.c b/aplay/aplay.c index 63a4e34..4f796d2 100644 --- a/aplay/aplay.c +++ b/aplay/aplay.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -452,6 +453,30 @@ static ssize_t xwrite(int fd, const void *buf, size_t count) return offset; } +static int open_ucm(snd_use_case_mgr_t **uc_mgr, char **pcm_name, const char *name) +{ + char *s, *p; + int err; + + s = strdup(name); + if (s == NULL) + return -ENOMEM; + p = strchr(s, '.'); + if (p == NULL) + return -EINVAL; + *p = '\0'; + err = snd_use_case_mgr_open(uc_mgr, s); + if (err < 0) + return err; + err = snd_use_case_get(*uc_mgr, p + 1, (const char **)pcm_name); + if (err < 0) { + error(_("UCM value '%s' error: %s"), p + 1, snd_strerror(err)); + snd_use_case_mgr_close(*uc_mgr); + return err; + } + return err; +} + static long parse_long(const char *str, int *err) { long val; @@ -528,6 +553,7 @@ int main(int argc, char *argv[]) int do_device_list = 0, do_pcm_list = 0, force_sample_format = 0; snd_pcm_info_t *info; FILE *direction; + snd_use_case_mgr_t *uc_mgr = NULL; #ifdef ENABLE_NLS setlocale(LC_ALL, ""); @@ -826,6 +852,16 @@ int main(int argc, char *argv[]) goto __end; } + if (strncmp(pcm_name, "ucm.", 4) == 0) { + err = open_ucm(&uc_mgr, &pcm_name, pcm_name + 4); + if (err < 0) { + error(_("UCM open error: %s"), snd_strerror(err)); + return 1; + } + if (verbose) + fprintf(stderr, _("Found UCM PCM device: %s\n"), pcm_name); + } + err = snd_pcm_open(&handle, pcm_name, stream, open_mode); if (err < 0) { error(_("audio open error: %s"), snd_strerror(err)); @@ -915,6 +951,8 @@ int main(int argc, char *argv[]) if (verbose==2) putchar('\n'); snd_pcm_close(handle); + if (uc_mgr) + snd_use_case_mgr_close(uc_mgr); handle = NULL; free(audiobuf); __end: diff --git a/topology/pre-process-dapm.c b/topology/pre-process-dapm.c index 49fe806..9355a86 100644 --- a/topology/pre-process-dapm.c +++ b/topology/pre-process-dapm.c @@ -423,112 +423,3 @@ err: free(sink_widget_name); return ret; } - -static int tplg_get_sample_size_from_format(const char *format) -{ - if (!strcmp(format, "s32le") || !strcmp(format, "s24le") || !strcmp(format, "float")) - return 4; - - if (!strcmp(format, "s16le")) - return 2; - - SNDERR("Unsupported format: %s\n", format); - return -EINVAL; -} - -int tplg_update_buffer_auto_attr(struct tplg_pre_processor *tplg_pp, - snd_config_t *buffer_cfg, snd_config_t *parent) -{ - snd_config_iterator_t i, next; - snd_config_t *n, *pipeline_cfg, *child; - const char *buffer_id, *format; - long periods, channels, sample_size; - long sched_period, rate, frames; - long buffer_size; - int err; - - if (snd_config_get_id(buffer_cfg, &buffer_id) < 0) - return -EINVAL; - - if (!parent) { - SNDERR("No parent for buffer %s\n", buffer_id); - return -EINVAL; - } - - /* acquire attributes from buffer config */ - snd_config_for_each(i, next, buffer_cfg) { - const char *id; - - n = snd_config_iterator_entry(i); - if (snd_config_get_id(n, &id) < 0) - continue; - - if (!strcmp(id, "periods")) { - if (snd_config_get_integer(n, &periods)) { - SNDERR("Invalid number of periods for buffer %s\n", buffer_id); - return -EINVAL; - } - } - - if (!strcmp(id, "channels")) { - if (snd_config_get_integer(n, &channels)) { - SNDERR("Invalid number of channels for buffer %s\n", buffer_id); - return -EINVAL; - } - } - - if (!strcmp(id, "format")) { - if (snd_config_get_string(n, &format)) { - SNDERR("Invalid format for buffer %s\n", buffer_id); - return -EINVAL; - } - } - } - - pipeline_cfg = tplg_object_get_instance_config(tplg_pp, parent); - - /* acquire some other attributes from parent pipeline config */ - snd_config_for_each(i, next, pipeline_cfg) { - const char *id; - - n = snd_config_iterator_entry(i); - if (snd_config_get_id(n, &id) < 0) - continue; - - if (!strcmp(id, "period")) { - if (snd_config_get_integer(n, &sched_period)) { - SNDERR("Invalid period for buffer %s\n", buffer_id); - return -EINVAL; - } - } - - if (!strcmp(id, "rate")) { - if (snd_config_get_integer(n, &rate)) { - SNDERR("Invalid rate for buffer %s\n", buffer_id); - return -EINVAL; - } - } - } - - /* calculate buffer size */ - sample_size = tplg_get_sample_size_from_format(format); - if (sample_size < 0) { - SNDERR("Invalid sample size value for %s\n", buffer_id); - return sample_size; - } - frames = (rate * sched_period) / 1000000; - buffer_size = periods * sample_size * channels * frames; - - /* add size child config to buffer config */ - err = tplg_config_make_add(&child, "size", SND_CONFIG_TYPE_INTEGER, buffer_cfg); - if (err < 0) { - SNDERR("Error creating size config for %s\n", buffer_id); - return err; - } - - err = snd_config_set_integer(child, buffer_size); - if (err < 0) - SNDERR("Error setting size config for %s\n", buffer_id); - - return err; -} diff --git a/topology/pre-process-object.c b/topology/pre-process-object.c index 57699ac..5ce3beb 100644 --- a/topology/pre-process-object.c +++ b/topology/pre-process-object.c @@ -1014,8 +1014,7 @@ const struct build_function_map object_build_map[] = { &hwcfg_config}, {"Base", "fe_dai", "dai", &tplg_build_fe_dai_object, NULL, &fe_dai_config}, {"Base", "route", "SectionGraph", &tplg_build_dapm_route_object, NULL, NULL}, - {"Widget", "buffer", "SectionWidget", &tplg_build_generic_object, - tplg_update_buffer_auto_attr, &widget_config}, + {"Widget", "buffer", "SectionWidget", &tplg_build_generic_object, NULL, &widget_config}, {"Widget", "", "SectionWidget", &tplg_build_generic_object, NULL, &widget_config}, {"Control", "mixer", "SectionControlMixer", &tplg_build_mixer_control, NULL, &mixer_control_config}, @@ -1087,10 +1086,13 @@ static int tplg_object_copy_and_add_param(struct tplg_pre_processor *tplg_pp, snd_config_t *attr_cfg, snd_config_t *search_config) { - snd_config_t *attr, *new; + snd_config_iterator_t first = snd_config_iterator_first(obj); + snd_config_t *attr, *new, *first_cfg; const char *id, *search_id; int ret; + first_cfg = snd_config_iterator_entry(first); + if (snd_config_get_id(attr_cfg, &id) < 0) return 0; @@ -1108,10 +1110,19 @@ static int tplg_object_copy_and_add_param(struct tplg_pre_processor *tplg_pp, return ret; } - ret = snd_config_add(obj, new); - if (ret < 0) { - snd_config_delete(new); - SNDERR("error adding attribute '%s' value to %s\n", id, search_id); + if (first_cfg) { + /* prepend the new config */ + ret = snd_config_add_before(first_cfg, new); + if (ret < 0) { + snd_config_delete(new); + SNDERR("error prepending attribute '%s' value to %s\n", id, search_id); + } + } else { + ret = snd_config_add(obj, new); + if (ret < 0) { + snd_config_delete(new); + SNDERR("error adding attribute '%s' value to %s\n", id, search_id); + } } return ret; @@ -1488,12 +1499,67 @@ snd_config_t *tplg_object_get_instance_config(struct tplg_pre_processor *tplg_pp return snd_config_iterator_entry(first); } +#if SND_LIB_VER(1, 2, 5) < SND_LIB_VERSION +static int pre_process_find_variable(snd_config_t **dst, const char *str, snd_config_t *config) +{ + snd_config_iterator_t i, next; + + snd_config_for_each(i, next, config) { + snd_config_t *n; + const char *id; + + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + if (strcmp(id, str)) + continue; + + /* found definition, copy config */ + return snd_config_copy(dst, n); + } + + return -EINVAL; +} +static int +pre_process_object_variables_expand_fcn(snd_config_t **dst, const char *str, void *private_data) +{ + + struct tplg_pre_processor *tplg_pp = private_data; + snd_config_t *object_cfg = tplg_pp->current_obj_cfg; + snd_config_t *conf_defines; + const char *object_id; + int ret; + + ret = snd_config_search(tplg_pp->input_cfg, "Define", &conf_defines); + if (ret < 0) + return 0; + + /* find variable from global definitions first */ + ret = pre_process_find_variable(dst, str, conf_defines); + if (ret >= 0) + return ret; + + if (snd_config_get_id(object_cfg, &object_id) < 0) + return -EINVAL; + + /* find variable from object attribute values if not found in global definitions */ + ret = pre_process_find_variable(dst, str, object_cfg); + if (ret < 0) + SNDERR("Failed to find definition for attribute %s in '%s' object\n", + str, object_id); + + return ret; +} +#endif + /* build object config and its child objects recursively */ static int tplg_build_object(struct tplg_pre_processor *tplg_pp, snd_config_t *new_obj, snd_config_t *parent) { snd_config_t *obj_local, *class_cfg; const struct build_function_map *map; + snd_config_iterator_t i, next; build_func builder; update_auto_attr_func auto_attr_updater; const char *id, *class_id; @@ -1534,6 +1600,46 @@ static int tplg_build_object(struct tplg_pre_processor *tplg_pp, snd_config_t *n return ret; } +#if SND_LIB_VER(1, 2, 5) < SND_LIB_VERSION + tplg_pp_config_debug(tplg_pp, obj_local); + + /* expand all non-compound type child configs in object */ + snd_config_for_each(i, next, obj_local) { + snd_config_t *n, *new; + const char *id, *s; + + n = snd_config_iterator_entry(i); + + if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND) + continue; + + if (snd_config_get_id(n, &id) < 0) + continue; + + if (snd_config_get_string(n, &s) < 0) + continue; + + if (*s != '$') + continue; + + tplg_pp->current_obj_cfg = obj_local; + + /* expand config */ + ret = snd_config_evaluate_string(&new, s, pre_process_object_variables_expand_fcn, + tplg_pp); + if (ret < 0) { + SNDERR("Failed to evaluate attributes %s in %s\n", id, class_id); + return ret; + } + + snd_config_set_id(new, id); + + ret = snd_config_merge(n, new, true); + if (ret < 0) + return ret; + } +#endif + /* * Build objects if object type is supported. * If not, process object attributes and add to parent's data section diff --git a/topology/pre-processor.c b/topology/pre-processor.c index 23963a7..90a1570 100644 --- a/topology/pre-processor.c +++ b/topology/pre-processor.c @@ -259,57 +259,6 @@ static int pre_process_defines(struct tplg_pre_processor *tplg_pp, const char *p return 0; } -static int pre_process_variables_expand_fcn(snd_config_t **dst, const char *str, - void *private_data) -{ - struct tplg_pre_processor *tplg_pp = private_data; - snd_config_iterator_t i, next; - snd_config_t *conf_defines; - int ret; - - ret = snd_config_search(tplg_pp->input_cfg, "Define", &conf_defines); - if (ret < 0) - return 0; - - /* find variable definition */ - snd_config_for_each(i, next, conf_defines) { - snd_config_t *n; - const char *id; - - n = snd_config_iterator_entry(i); - if (snd_config_get_id(n, &id) < 0) - continue; - - if (strcmp(id, str)) - continue; - - /* found definition. Match type and return appropriate config */ - if (snd_config_get_type(n) == SND_CONFIG_TYPE_STRING) { - const char *s; - - if (snd_config_get_string(n, &s) < 0) - continue; - - return snd_config_imake_string(dst, NULL, s); - } - - if (snd_config_get_type(n) == SND_CONFIG_TYPE_INTEGER) { - long v; - - if (snd_config_get_integer(n, &v) < 0) - continue; - - ret = snd_config_imake_integer(dst, NULL, v); - return ret; - } - - } - - fprintf(stderr, "No definition for variable %s\n", str); - - return -EINVAL; -} - static int pre_process_includes(struct tplg_pre_processor *tplg_pp, snd_config_t *top, const char *pre_processor_defs, const char *inc_path); @@ -558,14 +507,6 @@ int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_ fprintf(stderr, "Failed to process conditional includes in input config\n"); goto err; } - - /* expand pre-processor variables */ - err = snd_config_expand_custom(tplg_pp->input_cfg, tplg_pp->input_cfg, pre_process_variables_expand_fcn, - tplg_pp, &tplg_pp->input_cfg); - if (err < 0) { - fprintf(stderr, "Failed to expand pre-processor definitions in input config\n"); - goto err; - } #endif err = pre_process_config(tplg_pp, tplg_pp->input_cfg); diff --git a/topology/pre-processor.h b/topology/pre-processor.h index c534ead..6965a70 100644 --- a/topology/pre-processor.h +++ b/topology/pre-processor.h @@ -81,8 +81,6 @@ int tplg_build_pcm_caps_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, snd_config_t *parent); int tplg_parent_update(struct tplg_pre_processor *tplg_pp, snd_config_t *parent, const char *section_name, const char *item_name); -int tplg_update_buffer_auto_attr(struct tplg_pre_processor *tplg_pp, - snd_config_t *buffer_cfg, snd_config_t *parent); int tplg_add_object_data(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, snd_config_t *top, const char *array_name); diff --git a/topology/topology.h b/topology/topology.h index 11ee64e..4b1e8fd 100644 --- a/topology/topology.h +++ b/topology/topology.h @@ -25,6 +25,7 @@ struct tplg_pre_processor { snd_config_t *output_cfg; snd_output_t *output; snd_output_t *dbg_output; + snd_config_t *current_obj_cfg; }; int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size,