summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaroslav Kysela <perex@perex.cz>2022-05-13 16:07:38 +0200
committerJaroslav Kysela <perex@perex.cz>2022-05-13 17:25:29 +0200
commitd7d5c724edc3b707df6a1566c3ee6f4681858bd4 (patch)
treec5bd27cbffe4b17acf7bb0052b5b4722e21e782a
parent3e5a8943781d17361ffe79e03c23e76946fcd67f (diff)
downloadalsa-lib-d7d5c724edc3b707df6a1566c3ee6f4681858bd4.tar.gz
ucm: implement MacroDefine and Macro subtree evaluation
The arguments are set as temporary variables as /MACRO_NAME/_/ARGUMENT_NAME/. Example: # define new macro MyMacro with arguments ctl_name and ctl_value DefineMacro.MyMacro { BootSequence [ cset "name='${var:MyMacro_ctl_name}' ${var:MyMacro_ctl_value}" ] } # instantiate macro for Speaker control (short version) Macro.headphone.MyMacro "ctl_name='Speaker Switch',ctl_value=off" # instantiate macro for Mic control (second version) Macro.mic.MyMacro { ctl_name "Mic Switch" ctl_value "off" } Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r--src/ucm/main.c4
-rw-r--r--src/ucm/parser.c180
-rw-r--r--src/ucm/ucm_confdoc.h35
-rw-r--r--src/ucm/ucm_local.h5
-rw-r--r--src/ucm/ucm_subs.c6
-rw-r--r--src/ucm/utils.c31
6 files changed, 252 insertions, 9 deletions
diff --git a/src/ucm/main.c b/src/ucm/main.c
index 078cfd64..8ca76630 100644
--- a/src/ucm/main.c
+++ b/src/ucm/main.c
@@ -1398,6 +1398,10 @@ int snd_use_case_mgr_open(snd_use_case_mgr_t **uc_mgr,
if (err < 0)
goto _err;
+ err = snd_config_top(&mgr->macros);
+ if (err < 0)
+ goto _err;
+
mgr->card_name = strdup(card_name);
if (mgr->card_name == NULL) {
err = -ENOMEM;
diff --git a/src/ucm/parser.c b/src/ucm/parser.c
index 7bdaa8fe..830c37d2 100644
--- a/src/ucm/parser.c
+++ b/src/ucm/parser.c
@@ -327,8 +327,10 @@ static int evaluate_define(snd_use_case_mgr_t *uc_mgr,
free(var);
if (err < 0)
return err;
- uc_mgr_set_variable(uc_mgr, id, s);
+ err = uc_mgr_set_variable(uc_mgr, id, s);
free(s);
+ if (err < 0)
+ return err;
}
snd_config_delete(d);
@@ -337,6 +339,159 @@ static int evaluate_define(snd_use_case_mgr_t *uc_mgr,
}
/*
+ * Evaluate macro definitions (in-place delete)
+ */
+static int evaluate_define_macro(snd_use_case_mgr_t *uc_mgr,
+ snd_config_t *cfg)
+{
+ snd_config_t *d;
+ int err;
+
+ err = snd_config_search(cfg, "DefineMacro", &d);
+ if (err == -ENOENT)
+ return 1;
+ if (err < 0)
+ return err;
+
+ if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
+ uc_error("compound type expected for DefineMacro");
+ return -EINVAL;
+ }
+
+ if (uc_mgr->conf_format < 5) {
+ uc_error("DefineMacro is supported in v5+ syntax");
+ return -EINVAL;
+ }
+
+ err = snd_config_merge(uc_mgr->macros, d, 0);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr,
+ snd_config_t *dst,
+ snd_config_t *args)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *m, *mc, *a, *n;
+ const char *mid, *id;
+ char name[128], *var;
+ const char *s;
+ int err;
+
+ err = snd_config_get_id(args, &mid);
+ if (err < 0)
+ return err;
+ err = snd_config_search(uc_mgr->macros, mid, &m);
+ if (err < 0) {
+ uc_error("Macro '%s' is not defined", mid);
+ return err;
+ }
+
+ a = args;
+ if (snd_config_get_type(args) == SND_CONFIG_TYPE_STRING) {
+ err = snd_config_get_string(args, &s);
+ if (err < 0)
+ return err;
+ err = snd_config_load_string(&a, s, 0);
+ if (err < 0)
+ return err;
+ } else if (snd_config_get_type(args) != SND_CONFIG_TYPE_COMPOUND) {
+ return -EINVAL;
+ }
+
+ /* set arguments */
+ snd_config_for_each(i, next, a) {
+ n = snd_config_iterator_entry(i);
+ err = snd_config_get_id(n, &id);
+ if (err < 0)
+ return err;
+ err = snd_config_get_ascii(n, &var);
+ if (err < 0)
+ return err;
+ snprintf(name, sizeof(name), "%s_%s", mid, id);
+ err = uc_mgr_set_variable(uc_mgr, name, var);
+ free(var);
+ if (err < 0)
+ return err;
+ }
+
+ /* merge + substitute variables */
+ err = snd_config_copy(&mc, m);
+ if (err < 0)
+ goto __err_path;
+ err = uc_mgr_config_tree_merge(uc_mgr, dst, mc, NULL, NULL);
+ snd_config_delete(mc);
+
+ /* delete arguments */
+ snd_config_for_each(i, next, a) {
+ n = snd_config_iterator_entry(i);
+ err = snd_config_get_id(n, &id);
+ if (err < 0)
+ return err;
+ snprintf(name, sizeof(name), "%s_%s", mid, id);
+ err = uc_mgr_delete_variable(uc_mgr, name);
+ if (err < 0)
+ return err;
+ }
+
+__err_path:
+ if (a != args)
+ snd_config_delete(a);
+ return err;
+}
+
+/*
+ * Evaluate macro definitions and instances (in-place delete)
+ */
+static int evaluate_macro(snd_use_case_mgr_t *uc_mgr,
+ snd_config_t *cfg)
+{
+ snd_config_iterator_t i, i2, next, next2;
+ snd_config_t *d, *n, *n2;
+ int err, ret;
+
+ ret = evaluate_define_macro(uc_mgr, cfg);
+ if (ret < 0)
+ return ret;
+
+ err = snd_config_search(cfg, "Macro", &d);
+ if (err == -ENOENT)
+ return ret;
+ if (err < 0)
+ return err;
+
+ if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
+ uc_error("compound type expected for DefineMacro");
+ return -EINVAL;
+ }
+
+ if (uc_mgr->conf_format < 5) {
+ uc_error("Macro is supported in v5+ syntax");
+ return -EINVAL;
+ }
+
+ snd_config_for_each(i, next, d) {
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
+ uc_error("compound type expected for DefineMacro");
+ return -EINVAL;
+ }
+ snd_config_for_each(i2, next2, n) {
+ n2 = snd_config_iterator_entry(i2);
+ err = evaluate_macro1(uc_mgr, cfg, n2);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ snd_config_delete(d);
+
+ return 0;
+}
+
+/*
* Evaluate include (in-place)
*/
static int evaluate_include(snd_use_case_mgr_t *uc_mgr,
@@ -382,9 +537,15 @@ static int evaluate_condition(snd_use_case_mgr_t *uc_mgr,
int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
snd_config_t *cfg)
{
- int err1 = 0, err2 = 0, err3 = 0;
+ long iterations = 10000;
+ int err1 = 0, err2 = 0, err3 = 0, err4 = 0;
- while (err1 == 0 || err2 == 0 || err3 == 0) {
+ while (err1 == 0 || err2 == 0 || err3 == 0 || err4 == 0) {
+ if (iterations == 0) {
+ uc_error("Maximal inplace evaluation iterations number reached (recursive references?)");
+ return -EINVAL;
+ }
+ iterations--;
/* variables at first */
err1 = evaluate_define(uc_mgr, cfg);
if (err1 < 0)
@@ -393,13 +554,18 @@ int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
err2 = evaluate_include(uc_mgr, cfg);
if (err2 < 0)
return err2;
- /* include may define another variables */
+ /* include or macro may define another variables */
/* conditions may depend on them */
if (err2 == 0)
continue;
- err3 = evaluate_condition(uc_mgr, cfg);
+ err3 = evaluate_macro(uc_mgr, cfg);
if (err3 < 0)
return err3;
+ if (err3 == 0)
+ continue;
+ err4 = evaluate_condition(uc_mgr, cfg);
+ if (err4 < 0)
+ return err3;
}
return 0;
}
@@ -2458,6 +2624,10 @@ int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
goto __error;
err = parse_master_file(uc_mgr, cfg);
+ if (uc_mgr->macros) {
+ snd_config_delete(uc_mgr->macros);
+ uc_mgr->macros = NULL;
+ }
snd_config_delete(cfg);
if (err < 0) {
uc_mgr_free_ctl_list(uc_mgr);
diff --git a/src/ucm/ucm_confdoc.h b/src/ucm/ucm_confdoc.h
index 430afa27..090bd86c 100644
--- a/src/ucm/ucm_confdoc.h
+++ b/src/ucm/ucm_confdoc.h
@@ -452,6 +452,41 @@ substrings are stored to a separate variable with the sequence number postfix.
Variables can be substituted using the `${var:rval1}` reference for example.
+### Macros
+
+Macros were added for *Syntax* version *5*. The *DefineMacro* defines new
+macro like:
+
+~~~{.html}
+DefineMacro.macro1 {
+ Define.a "${var:macro1_arg1}"
+ Define.b "${var:macro1_other}"
+ # Device or any other block may be defined here...
+}
+~~~
+
+The arguments in the macro are refered as the variables with the macro
+name prefix and underscore (*'_'*) delimiter. The configuration block
+in the DefineMacro subtree is always evaluated (including arguments
+and variables) at the time of the instantiation.
+
+The macros can be instantiated (expanded) using:
+
+~~~{.html}
+# short version
+Macro.id1.macro1 "arg1='something 1',other='other x'"
+
+# long version
+Macro.id1.macro1 {
+ arg1 'something 1'
+ other 'other x'
+}
+~~~
+
+The second identifier (in example as *id1*) must be unique, but the contents
+is ignored. It just differentiate the items in the subtree (allowing multiple
+instances for one macro).
+
### Conditions
The configuration tree evaluation supports the conditions - *If* blocks. Each *If* blocks
diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h
index e6ebe0f3..3f59bb2c 100644
--- a/src/ucm/ucm_local.h
+++ b/src/ucm/ucm_local.h
@@ -262,6 +262,9 @@ struct snd_use_case_mgr {
/* list of opened control devices */
struct list_head ctl_list;
+ /* tree with macros */
+ snd_config_t *macros;
+
/* local library configuration */
snd_config_t *local_config;
@@ -334,6 +337,8 @@ int uc_mgr_set_variable(snd_use_case_mgr_t *uc_mgr,
const char *name,
const char *val);
+int uc_mgr_delete_variable(snd_use_case_mgr_t *uc_mgr, const char *name);
+
int uc_mgr_get_substituted_value(snd_use_case_mgr_t *uc_mgr,
char **_rvalue,
const char *value);
diff --git a/src/ucm/ucm_subs.c b/src/ucm/ucm_subs.c
index fd3dcc6d..8b42b889 100644
--- a/src/ucm/ucm_subs.c
+++ b/src/ucm/ucm_subs.c
@@ -866,6 +866,12 @@ int uc_mgr_substitute_tree(snd_use_case_mgr_t *uc_mgr, snd_config_t *node)
}
return 0;
}
+ /* exception - macros are evaluated when instantied */
+ err = snd_config_get_id(node, &id);
+ if (err < 0)
+ return err;
+ if (id && strcmp(id, "DefineMacro") == 0)
+ return 0;
snd_config_for_each(i, next, node) {
n = snd_config_iterator_entry(i);
err = uc_mgr_substitute_tree(uc_mgr, n);
diff --git a/src/ucm/utils.c b/src/ucm/utils.c
index 185170f9..13e084f2 100644
--- a/src/ucm/utils.c
+++ b/src/ucm/utils.c
@@ -400,6 +400,14 @@ int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg)
return 0;
}
+static void uc_mgr_free_value1(struct ucm_value *val)
+{
+ free(val->name);
+ free(val->data);
+ list_del(&val->list);
+ free(val);
+}
+
void uc_mgr_free_value(struct list_head *base)
{
struct list_head *pos, *npos;
@@ -407,10 +415,7 @@ void uc_mgr_free_value(struct list_head *base)
list_for_each_safe(pos, npos, base) {
val = list_entry(pos, struct ucm_value, list);
- free(val->name);
- free(val->data);
- list_del(&val->list);
- free(val);
+ uc_mgr_free_value1(val);
}
}
@@ -704,6 +709,22 @@ int uc_mgr_set_variable(snd_use_case_mgr_t *uc_mgr, const char *name,
return 0;
}
+int uc_mgr_delete_variable(snd_use_case_mgr_t *uc_mgr, const char *name)
+{
+ struct list_head *pos;
+ struct ucm_value *curr;
+
+ list_for_each(pos, &uc_mgr->variable_list) {
+ curr = list_entry(pos, struct ucm_value, list);
+ if (strcmp(curr->name, name) == 0) {
+ uc_mgr_free_value1(curr);
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr)
{
struct list_head *pos, *npos;
@@ -745,6 +766,8 @@ void uc_mgr_free(snd_use_case_mgr_t *uc_mgr)
{
if (uc_mgr->local_config)
snd_config_delete(uc_mgr->local_config);
+ if (uc_mgr->macros)
+ snd_config_delete(uc_mgr->macros);
uc_mgr_free_verb(uc_mgr);
uc_mgr_free_ctl_list(uc_mgr);
free(uc_mgr->card_name);