summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaroslav Kysela <perex@perex.cz>2022-05-18 13:10:35 +0200
committerJaroslav Kysela <perex@perex.cz>2022-05-18 13:10:35 +0200
commitc3a5d32ac37b0a09c77e98cd3b1be888bb0141e4 (patch)
treef04d9e43aec93aa434bb91a0bd60cc58e523bca4
parent25e44bbeb976417eebba7323db779a5c44a1a389 (diff)
downloadalsa-lib-c3a5d32ac37b0a09c77e98cd3b1be888bb0141e4.tar.gz
ucm: add support for verb variants
The bellow configuration example creates two verbs ("HiFi" and "HiFi 7+1") with different playback channels for the "Speaker" device. SectionUseCase."HiFi" { File "HiFi.conf" Variant."HiFi" { Comment "Default" } Variant."HiFi 7+1" { Comment "HiFi 7.1" } } SectionDevice."Speaker" { Value { PlaybackChannels 2 } Variant."HiFi 7+1".Value { PlaybackChannels 8 } } Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r--src/ucm/parser.c207
-rw-r--r--src/ucm/ucm_local.h2
2 files changed, 189 insertions, 20 deletions
diff --git a/src/ucm/parser.c b/src/ucm/parser.c
index 7e4e4ced..cbefbd13 100644
--- a/src/ucm/parser.c
+++ b/src/ucm/parser.c
@@ -531,8 +531,7 @@ static int evaluate_include(snd_use_case_mgr_t *uc_mgr,
/*
* Evaluate condition (in-place)
*/
-static int evaluate_condition(snd_use_case_mgr_t *uc_mgr,
- snd_config_t *cfg)
+static int evaluate_condition(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
{
snd_config_t *n;
int err;
@@ -549,15 +548,67 @@ static int evaluate_condition(snd_use_case_mgr_t *uc_mgr,
}
/*
+ * Evaluate variant (in-place)
+ */
+static int evaluate_variant(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n, *c;
+ const char *id;
+ int err;
+
+ err = snd_config_search(cfg, "Variant", &c);
+ if (err == -ENOENT)
+ return 1;
+ if (err < 0)
+ return err;
+
+ if (uc_mgr->conf_format < 6) {
+ uc_error("Variant is supported in v6+ syntax");
+ return -EINVAL;
+ }
+
+ if (uc_mgr->parse_master_section)
+ return 1;
+
+ if (uc_mgr->parse_variant == NULL)
+ goto __ret;
+
+ snd_config_for_each(i, next, c) {
+ n = snd_config_iterator_entry(i);
+
+ if (snd_config_get_id(n, &id) < 0)
+ return -EINVAL;
+
+ if (strcmp(id, uc_mgr->parse_variant))
+ continue;
+
+ err = uc_mgr_evaluate_inplace(uc_mgr, n);
+ if (err < 0)
+ return err;
+
+ err = uc_mgr_config_tree_merge(uc_mgr, cfg, n, NULL, NULL);
+ if (err < 0)
+ return err;
+ snd_config_delete(c);
+ return 0;
+ }
+
+__ret:
+ snd_config_delete(c);
+ return 1;
+}
+
+/*
* In-place evaluate
*/
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;
+ int err1 = 0, err2 = 0, err3 = 0, err4 = 0, err5 = 0;
- while (err1 == 0 || err2 == 0 || err3 == 0 || err4 == 0) {
+ while (err1 == 0 || err2 == 0 || err3 == 0 || err4 == 0 || err5 == 0) {
if (iterations == 0) {
uc_error("Maximal inplace evaluation iterations number reached (recursive references?)");
return -EINVAL;
@@ -575,20 +626,25 @@ int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
/* conditions may depend on them */
if (err2 == 0)
continue;
+ err3 = evaluate_variant(uc_mgr, cfg);
+ if (err3 < 0)
+ return err3;
+ if (err3 == 0)
+ continue;
uc_mgr->macro_hops++;
if (uc_mgr->macro_hops > 100) {
uc_error("Maximal macro hops reached!");
return -EINVAL;
}
- err3 = evaluate_macro(uc_mgr, cfg);
+ err4 = evaluate_macro(uc_mgr, cfg);
uc_mgr->macro_hops--;
- if (err3 < 0)
- return err3;
- if (err3 == 0)
- continue;
- err4 = evaluate_condition(uc_mgr, cfg);
if (err4 < 0)
- return err3;
+ return err4;
+ if (err4 == 0)
+ continue;
+ err5 = evaluate_condition(uc_mgr, cfg);
+ if (err5 < 0)
+ return err5;
}
return 0;
}
@@ -1999,6 +2055,65 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
}
/*
+ * Parse variant information
+ */
+static int parse_variant(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
+ char **_vfile, char **_vcomment)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ char *file = NULL, *comment = NULL;
+ int err;
+
+ /* parse master config sections */
+ snd_config_for_each(i, next, cfg) {
+ const char *id;
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* get use case verb file name */
+ if (strcmp(id, "File") == 0) {
+ if (_vfile) {
+ err = parse_string_substitute3(uc_mgr, n, &file);
+ if (err < 0) {
+ uc_error("failed to get File");
+ goto __error;
+ }
+ }
+ continue;
+ }
+
+ /* get optional use case comment */
+ if (strncmp(id, "Comment", 7) == 0) {
+ if (_vcomment) {
+ err = parse_string_substitute3(uc_mgr, n, &comment);
+ if (err < 0) {
+ uc_error("error: failed to get Comment");
+ goto __error;
+ }
+ }
+ continue;
+ }
+
+ uc_error("unknown field '%s' in Variant section", id);
+ err = -EINVAL;
+ goto __error;
+ }
+
+ if (_vfile)
+ *_vfile = file;
+ if (_vcomment)
+ *_vcomment = comment;
+ return 0;
+
+__error:
+ free(file);
+ free(comment);
+ return err;
+}
+
+/*
* Parse master section for "Use Case" and "File" tags.
*/
static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
@@ -2006,8 +2121,9 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
void *data2 ATTRIBUTE_UNUSED)
{
snd_config_iterator_t i, next;
- snd_config_t *n;
+ snd_config_t *n, *variant = NULL;
char *use_case_name, *file = NULL, *comment = NULL;
+ bool variant_ok = false;
int err;
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
@@ -2022,7 +2138,9 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
}
/* in-place evaluation */
+ uc_mgr->parse_master_section = 1;
err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
+ uc_mgr->parse_master_section = 0;
if (err < 0)
goto __error;
@@ -2053,20 +2171,69 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
continue;
}
- uc_error("unknown field %s in master section");
- }
+ if (uc_mgr->conf_format >= 6 && strcmp(id, "Variant") == 0) {
+ snd_config_iterator_t i2, next2;
+ variant = n;
+ snd_config_for_each(i2, next2, n) {
+ const char *id2;
+ snd_config_t *n2;
+ n2 = snd_config_iterator_entry(i2);
+ if (snd_config_get_id(n2, &id2) < 0)
+ continue;
+ err = uc_mgr_evaluate_inplace(uc_mgr, n2);
+ if (err < 0)
+ goto __error;
+ if (strcmp(use_case_name, id2) == 0)
+ variant_ok = true;
+ }
+ continue;
+ }
- uc_dbg("use_case_name %s file '%s'", use_case_name, file);
+ uc_error("unknown field '%s' in SectionUseCase", id);
+ }
- /* do we have both use case name and file ? */
- if (!file) {
- uc_error("error: use case missing file");
+ if (variant && !variant_ok) {
+ uc_error("error: undefined variant '%s'", use_case_name);
err = -EINVAL;
goto __error;
}
- /* parse verb file */
- err = parse_verb_file(uc_mgr, use_case_name, comment, file);
+ if (!variant) {
+ uc_dbg("use_case_name %s file '%s'", use_case_name, file);
+
+ /* do we have both use case name and file ? */
+ if (!file) {
+ uc_error("error: use case missing file");
+ err = -EINVAL;
+ goto __error;
+ }
+
+ /* parse verb file */
+ err = parse_verb_file(uc_mgr, use_case_name, comment, file);
+ } else {
+ /* parse variants */
+ snd_config_for_each(i, next, variant) {
+ char *vfile, *vcomment;
+ const char *id;
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+ if (!parse_is_name_safe(id)) {
+ err = -EINVAL;
+ goto __error;
+ }
+ err = parse_variant(uc_mgr, n, &vfile, &vcomment);
+ if (err < 0)
+ break;
+ uc_mgr->parse_variant = id;
+ err = parse_verb_file(uc_mgr, id,
+ vcomment ? vcomment : comment,
+ vfile ? vfile : file);
+ uc_mgr->parse_variant = NULL;
+ free(vfile);
+ free(vcomment);
+ }
+ }
__error:
free(use_case_name);
diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h
index 6bdecce4..15fbc1b5 100644
--- a/src/ucm/ucm_local.h
+++ b/src/ucm/ucm_local.h
@@ -228,6 +228,8 @@ struct snd_use_case_mgr {
int conf_format;
unsigned int ucm_card_number;
int suppress_nodev_errors;
+ const char *parse_variant;
+ int parse_master_section;
/* UCM cards list */
struct list_head cards_list;