From ffc63f76defc799a37a0fec734093b181076d67f Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 22 May 2024 16:29:34 -0700 Subject: [PATCH] topology: pre-processor: Introduce a new feature for subtree copies Introduce a new kyword "SubTreeCopy" for extneding existing conf nodes with additional nodes. This feature is useful for extending previous pipeline class definitions with the addition of one or more widgets without having to duplicate everything in the new class definition. For example: Consider a pipeline class definition as below. Note that only the widgets & routes are shown here. Class.Pipeline.mixout-gain-dai-copier-playback { Object.Widget { mixout."1" {} dai-copier."1" {} gain."1" {} pipeline."1" {} } Object.Base { !route [ { source mixout.$index.1 sink gain.$index.1 } ] } } If we want to extend this pipeline with the addition of an eqiir/eqfir, we can create a SubTreeCopy node with type extend as follows: Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback { SubTreeCopy.baseclass { source "Class.Pipeline.mixout-gain-dai-copier-playback" type extend tree { Object.Widget { eqiir.1 {} eqfir.1 {} } Object.Base { !route [ { source gain.$index.1 sink eqiir.$index.1 } { source eqiir.$index.1 sink eqfir.$index.1 } ] } } } } Note that the target is left undefined, which means that the newly created subtree will be merged to the parent node that contains the "SubTreeCopy" node i.e. in this case Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback". But if we want to modify an existing pipeline class while modifying the order of widgets and/or inserting new widgets, we should use the type "override" instead. This allows for adding new widgets to the list of widgets in the base class definition while also allowing overriding the routes to allow inserting the new widgets and reordering the widgets in the base class. For example, if we want to add a drc widget between the gain and the eqiir modules in the above class, we can do the following: Class.Pipeline.mixout-efx-dai-copier-playback { # This copy will override all widgets/routes in the base class SubTreeCopy.baseclass { source "Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback" target "Class.Pipeline" type override tree { Object.Widget { drc.1 {} } Object.Base { !route [ { source mixout.$index.1 sink gain.$index.1 } { source gain.$index.1 sink drc.$index.1 } { source drc.$index.1 sink eqiir.$index.1 } { source eqiir.$index.1 sink eqfir.$index.1 } ] } } } # Explicitly copy the widgets from the base class now SubTreeCopy.widgets { source "Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback.Object.Widget" target "Class.Pipeline.mixout-efx-dai-copier-playback.Object.Widget" } } Signed-off-by: Ranjani Sridharan --- topology/pre-processor.c | 172 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/topology/pre-processor.c b/topology/pre-processor.c index 01f8a5de..66b5a589 100644 --- a/topology/pre-processor.c +++ b/topology/pre-processor.c @@ -921,6 +921,172 @@ static int pre_process_arrays(struct tplg_pre_processor *tplg_pp, snd_config_t * return 0; } +static int pre_process_subtree_copy(struct tplg_pre_processor *tplg_pp, snd_config_t *curr, + snd_config_t *top) +{ + snd_config_iterator_t i, next; + snd_config_t *subtrees; + int ret; + + ret = snd_config_search(curr, "SubTreeCopy", &subtrees); + if (ret < 0) + return 0; + + snd_config_for_each(i, next, subtrees) { + snd_config_t *n, *source_cfg, *target_cfg, *type_cfg; + snd_config_t *source_tree, *target_tree, *tmp, *subtree_cfg; + const char *id, *source_id; + const char *type = NULL; + const char *target_id = NULL; + bool override = false; + + n = snd_config_iterator_entry(i); + ret = snd_config_get_id(n, &id); + if (ret < 0) { + SNDERR("Failed to get ID for subtree copy\n"); + return ret; + } + + /* get the type of copy ex: override/extend if set, by default override is false */ + ret = snd_config_search(n, "type", &type_cfg); + if (ret >= 0) { + ret = snd_config_get_string(type_cfg, &type); + if (ret >= 0) { + if (strcmp(type, "override") && strcmp(type, "extend")) { + SNDERR("Invalid value for sub tree copy type %s\n", type); + return ret; + } + + if (!strcmp(type, "override")) + override = true; + } + } + + ret = snd_config_search(n, "source", &source_cfg); + if (ret < 0) { + SNDERR("failed to get source config for subtree %s\n", id); + return ret; + } + + /* if the target node is not set, the subtree will be copied to current node */ + ret = snd_config_search(n, "target", &target_cfg); + if (ret >= 0) { + snd_config_t *temp_target; + char *s; + + ret = snd_config_get_string(target_cfg, &target_id); + if (ret < 0) { + SNDERR("Invalid target id for subtree %s\n", id); + return ret; + } + + /* + * create a temporary node with target_id and merge with top to avoid + * failure in case the target node ID doesn't exist already + */ + s = tplg_snprintf("%s {}", target_id); + if (!s) + return -ENOMEM; + + ret = snd_config_load_string(&temp_target, s, 0); + free(s); + if (ret < 0) { + SNDERR("Cannot create temp node with target id %s\n", target_id); + return ret; + } + + ret = snd_config_merge(top, temp_target, false); + if (ret < 0) { + SNDERR("Cannot merge temp node with target id %s\n", target_id); + return ret; + } + + ret = snd_config_search(top, target_id, &target_tree); + if (ret < 0) { + SNDERR("failed to get target tree %s\n", target_id); + return ret; + } + } else { + target_tree = curr; + } + + /* get the source tree node */ + ret = snd_config_get_string(source_cfg, &source_id); + if (ret < 0) { + SNDERR("Invalid source node id for subtree %s\n", id); + return ret; + } + + ret = snd_config_search(top, source_id, &source_tree); + if (ret < 0) { + SNDERR("failed to get source tree %s\n", source_id); + return ret; + } + + /* get the subtree to be merged */ + ret = snd_config_search(n, "tree", &subtree_cfg); + if (ret < 0) + subtree_cfg = NULL; + + /* make a temp copy of the source tree */ + ret = snd_config_copy(&tmp, source_tree); + if (ret < 0) { + SNDERR("failed to copy source tree for subtreecopy %s\n", id); + return ret; + } + + /* merge the current block with the source tree */ + ret = snd_config_merge(tmp, subtree_cfg, override); + if (ret < 0) { + snd_config_delete(tmp); + SNDERR("Failed to merge source tree w/ subtree %s\n", id); + return ret; + } + + /* merge the merged block to the target tree */ + ret = snd_config_merge(target_tree, tmp, override); + if (ret < 0) { + snd_config_delete(tmp); + SNDERR("Failed to merge subtree %s w/ target\n", id); + return ret; + } + } + + /* all subtree copies have been processed, remove the node */ + snd_config_delete(subtrees); + + return 0; +} + +static int pre_process_subtree_copies(struct tplg_pre_processor *tplg_pp, snd_config_t *top, + snd_config_t *curr) +{ + snd_config_iterator_t i, next; + int ret; + + if (snd_config_get_type(curr) != SND_CONFIG_TYPE_COMPOUND) + return 0; + + /* process subtreecopies at this node */ + ret = pre_process_subtree_copy(tplg_pp, curr, top); + if (ret < 0) + return ret; + + /* process subtreecopies at all child nodes */ + snd_config_for_each(i, next, curr) { + snd_config_t *n; + + n = snd_config_iterator_entry(i); + + /* process subtreecopies at this node */ + ret = pre_process_subtree_copies(tplg_pp, top, n); + if (ret < 0) + return ret; + } + + return 0; +} + #endif /* version < 1.2.6 */ int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size, @@ -980,6 +1146,12 @@ int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_ fprintf(stderr, "Failed to process object arrays in input config\n"); goto err; } + + err = pre_process_subtree_copies(tplg_pp, tplg_pp->input_cfg, tplg_pp->input_cfg); + if (err < 0) { + SNDERR("Failed to process subtree copies in input config\n"); + goto err; + } #endif err = pre_process_config(tplg_pp, tplg_pp->input_cfg);