From 9e933a08edb853144b02a4fc0dd6762229e3fddd Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 17 Nov 2025 18:36:32 +0100 Subject: [PATCH] ucm: add possibility to inline Verb configurations to the main configuration file Signed-off-by: Jaroslav Kysela --- src/ucm/parser.c | 122 ++++++++++++++++++++++++++++-------------- src/ucm/ucm_confdoc.h | 27 +++++++++- 2 files changed, 107 insertions(+), 42 deletions(-) diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 2ccb1126..7cd3cbff 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -1945,9 +1945,9 @@ static int parse_verb(snd_use_case_mgr_t *uc_mgr, } /* - * Parse a Use case verb file. + * Parse a Use case verb configuration. * - * This file contains the following :- + * This configuration contains the following :- * o Verb enable and disable sequences. * o Supported Device enable and disable sequences for verb. * o Supported Modifier enable and disable sequences for verb @@ -1955,15 +1955,15 @@ static int parse_verb(snd_use_case_mgr_t *uc_mgr, * o Optional PCM device ID for verb and modifiers * o Alias kcontrols IDs for master and volumes and mutes. */ -static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, - const char *use_case_name, - const char *comment, - const char *file) +static int parse_verb_config(snd_use_case_mgr_t *uc_mgr, + const char *use_case_name, + const char *comment, + snd_config_t *cfg, + const char *what) { snd_config_iterator_t i, next; snd_config_t *n; struct use_case_verb *verb; - snd_config_t *cfg; int err; /* allocate verb */ @@ -1992,15 +1992,10 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, return -ENOMEM; } - /* open Verb file for reading */ - err = uc_mgr_config_load_file(uc_mgr, file, &cfg); - if (err < 0) - return err; - /* in-place evaluation */ err = uc_mgr_evaluate_inplace(uc_mgr, cfg); if (err < 0) - goto _err; + return err; /* parse master config sections */ snd_config_for_each(i, next, cfg) { @@ -2013,8 +2008,8 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, if (strcmp(id, "SectionVerb") == 0) { err = parse_verb(uc_mgr, verb, n); if (err < 0) { - snd_error(UCM, "%s failed to parse verb", file); - goto _err; + snd_error(UCM, "%s failed to parse verb", what); + return err; } continue; } @@ -2024,8 +2019,8 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, err = parse_compound(uc_mgr, n, parse_device_name, verb, NULL); if (err < 0) { - snd_error(UCM, "%s failed to parse device", file); - goto _err; + snd_error(UCM, "%s failed to parse device", what); + return err; } continue; } @@ -2035,8 +2030,8 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, err = parse_compound(uc_mgr, n, parse_modifier_name, verb, NULL); if (err < 0) { - snd_error(UCM, "%s failed to parse modifier", file); - goto _err; + snd_error(UCM, "%s failed to parse modifier", what); + return err; } continue; } @@ -2045,8 +2040,8 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, if (strcmp(id, "RenameDevice") == 0) { err = parse_dev_name_list(uc_mgr, n, &verb->rename_list); if (err < 0) { - snd_error(UCM, " %s failed to parse device rename", file); - goto _err; + snd_error(UCM, "%s failed to parse device rename", what); + return err; } continue; } @@ -2055,8 +2050,8 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, if (strcmp(id, "RemoveDevice") == 0) { err = parse_dev_name_list(uc_mgr, n, &verb->remove_list); if (err < 0) { - snd_error(UCM, "%s failed to parse device remove", file); - goto _err; + snd_error(UCM, "%s failed to parse device remove", what); + return err; } continue; } @@ -2065,18 +2060,16 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) { err = parse_libconfig(uc_mgr, n); if (err < 0) { - snd_error(UCM, "failed to parse LibConfig"); - goto _err; + snd_error(UCM, "%s failed to parse LibConfig", what); + return err; } continue; } } - snd_config_delete(cfg); - /* use case verb must have at least 1 device */ if (list_empty(&verb->device_list)) { - snd_error(UCM, "no use case device defined", file); + snd_error(UCM, "no use case device defined"); return -EINVAL; } @@ -2088,10 +2081,6 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, } return 0; - - _err: - snd_config_delete(cfg); - return err; } /* @@ -2161,7 +2150,7 @@ 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, *variant = NULL; + snd_config_t *n, *variant = NULL, *config = NULL; char *use_case_name, *file = NULL, *comment = NULL; bool variant_ok = false; int err; @@ -2201,6 +2190,22 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, continue; } + /* get use case verb configuration block (syntax version 8+) */ + if (strcmp(id, "Config") == 0) { + if (uc_mgr->conf_format < 8) { + snd_error(UCM, "Config is supported in v8+ syntax"); + err = -EINVAL; + goto __error; + } + if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { + snd_error(UCM, "compound type expected for Config"); + err = -EINVAL; + goto __error; + } + config = n; + continue; + } + /* get optional use case comment */ if (strncmp(id, "Comment", 7) == 0) { err = parse_string_substitute3(uc_mgr, n, &comment); @@ -2238,18 +2243,38 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, goto __error; } + /* check mutual exclusivity of File and Config */ + if (file && config) { + snd_error(UCM, "both File and Config specified in SectionUseCase"); + err = -EINVAL; + goto __error; + } + if (!variant) { snd_debug(UCM, "use_case_name %s file '%s'", use_case_name, file); - /* do we have both use case name and file ? */ - if (!file) { - snd_error(UCM, "use case missing file"); + /* do we have both use case name and (file or config) ? */ + if (!file && !config) { + snd_error(UCM, "use case missing file or config"); err = -EINVAL; goto __error; } - /* parse verb file */ - err = parse_verb_file(uc_mgr, use_case_name, comment, file); + /* parse verb from file or config */ + if (file) { + snd_config_t *cfg; + /* load config from file */ + err = uc_mgr_config_load_file(uc_mgr, file, &cfg); + if (err < 0) + goto __error; + /* parse the config */ + err = parse_verb_config(uc_mgr, use_case_name, comment, cfg, file); + snd_config_delete(cfg); + } else { + /* inline config - parse directly */ + err = parse_verb_config(uc_mgr, use_case_name, comment, config, + comment ? comment : use_case_name); + } } else { /* parse variants */ struct list_head orig_variable_list; @@ -2303,9 +2328,24 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, if (err < 0) break; uc_mgr->parse_variant = id; - err = parse_verb_file(uc_mgr, id, - vcomment ? vcomment : comment, - vfile ? vfile : file); + if (vfile || file) { + snd_config_t *cfg; + const char *fname = vfile ? vfile : file; + /* load config from file */ + err = uc_mgr_config_load_file(uc_mgr, fname, &cfg); + if (err >= 0) { + err = parse_verb_config(uc_mgr, id, + vcomment ? vcomment : comment, + cfg, fname); + snd_config_delete(cfg); + } + } else { + /* inline config from variant */ + err = parse_verb_config(uc_mgr, id, + vcomment ? vcomment : comment, + config, + vcomment ? vcomment : (comment ? comment : id)); + } uc_mgr->parse_variant = NULL; free(vfile); free(vcomment); diff --git a/src/ucm/ucm_confdoc.h b/src/ucm/ucm_confdoc.h index 924f640d..cb614114 100644 --- a/src/ucm/ucm_confdoc.h +++ b/src/ucm/ucm_confdoc.h @@ -63,7 +63,7 @@ use case verbs for that sound card. i.e.: # Example master file for blah sound card # By Joe Blogs -Syntax 7 +Syntax 8 # Use Case name for user interface Comment "Nice Abstracted Soundcard" @@ -80,6 +80,31 @@ SectionUseCase."HiFi" { Comment "Play and record HiFi quality Music." } +# Since Syntax 8, you can also use Config to specify configuration inline +# instead of referencing an external file. Only one of File or Config can be used. + +SectionUseCase."Inline Example" { + Comment "Example with inline configuration" + Config { + SectionVerb { + EnableSequence [ + cset "name='Power Save' off" + ] + DisableSequence [ + cset "name='Power Save' on" + ] + } + SectionDevice."Speaker" { + EnableSequence [ + cset "name='Speaker Switch' on" + ] + DisableSequence [ + cset "name='Speaker Switch' off" + ] + } + } +} + # Define Value defaults ValueDefaults { -- 2.47.3