ucm: add possibility to inline Verb configurations to the main configuration file

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
Jaroslav Kysela 2025-11-17 18:36:32 +01:00
parent a8620e814b
commit 9e933a08ed
2 changed files with 107 additions and 42 deletions

View file

@ -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 Verb enable and disable sequences.
* o Supported Device enable and disable sequences for verb. * o Supported Device enable and disable sequences for verb.
* o Supported Modifier 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 Optional PCM device ID for verb and modifiers
* o Alias kcontrols IDs for master and volumes and mutes. * o Alias kcontrols IDs for master and volumes and mutes.
*/ */
static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, static int parse_verb_config(snd_use_case_mgr_t *uc_mgr,
const char *use_case_name, const char *use_case_name,
const char *comment, const char *comment,
const char *file) snd_config_t *cfg,
const char *what)
{ {
snd_config_iterator_t i, next; snd_config_iterator_t i, next;
snd_config_t *n; snd_config_t *n;
struct use_case_verb *verb; struct use_case_verb *verb;
snd_config_t *cfg;
int err; int err;
/* allocate verb */ /* allocate verb */
@ -1992,15 +1992,10 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
return -ENOMEM; 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 */ /* in-place evaluation */
err = uc_mgr_evaluate_inplace(uc_mgr, cfg); err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
if (err < 0) if (err < 0)
goto _err; return err;
/* parse master config sections */ /* parse master config sections */
snd_config_for_each(i, next, cfg) { 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) { if (strcmp(id, "SectionVerb") == 0) {
err = parse_verb(uc_mgr, verb, n); err = parse_verb(uc_mgr, verb, n);
if (err < 0) { if (err < 0) {
snd_error(UCM, "%s failed to parse verb", file); snd_error(UCM, "%s failed to parse verb", what);
goto _err; return err;
} }
continue; continue;
} }
@ -2024,8 +2019,8 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
err = parse_compound(uc_mgr, n, err = parse_compound(uc_mgr, n,
parse_device_name, verb, NULL); parse_device_name, verb, NULL);
if (err < 0) { if (err < 0) {
snd_error(UCM, "%s failed to parse device", file); snd_error(UCM, "%s failed to parse device", what);
goto _err; return err;
} }
continue; continue;
} }
@ -2035,8 +2030,8 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
err = parse_compound(uc_mgr, n, err = parse_compound(uc_mgr, n,
parse_modifier_name, verb, NULL); parse_modifier_name, verb, NULL);
if (err < 0) { if (err < 0) {
snd_error(UCM, "%s failed to parse modifier", file); snd_error(UCM, "%s failed to parse modifier", what);
goto _err; return err;
} }
continue; continue;
} }
@ -2045,8 +2040,8 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
if (strcmp(id, "RenameDevice") == 0) { if (strcmp(id, "RenameDevice") == 0) {
err = parse_dev_name_list(uc_mgr, n, &verb->rename_list); err = parse_dev_name_list(uc_mgr, n, &verb->rename_list);
if (err < 0) { if (err < 0) {
snd_error(UCM, " %s failed to parse device rename", file); snd_error(UCM, "%s failed to parse device rename", what);
goto _err; return err;
} }
continue; continue;
} }
@ -2055,8 +2050,8 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
if (strcmp(id, "RemoveDevice") == 0) { if (strcmp(id, "RemoveDevice") == 0) {
err = parse_dev_name_list(uc_mgr, n, &verb->remove_list); err = parse_dev_name_list(uc_mgr, n, &verb->remove_list);
if (err < 0) { if (err < 0) {
snd_error(UCM, "%s failed to parse device remove", file); snd_error(UCM, "%s failed to parse device remove", what);
goto _err; return err;
} }
continue; 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) { if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) {
err = parse_libconfig(uc_mgr, n); err = parse_libconfig(uc_mgr, n);
if (err < 0) { if (err < 0) {
snd_error(UCM, "failed to parse LibConfig"); snd_error(UCM, "%s failed to parse LibConfig", what);
goto _err; return err;
} }
continue; continue;
} }
} }
snd_config_delete(cfg);
/* use case verb must have at least 1 device */ /* use case verb must have at least 1 device */
if (list_empty(&verb->device_list)) { 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; return -EINVAL;
} }
@ -2088,10 +2081,6 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
} }
return 0; 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) void *data2 ATTRIBUTE_UNUSED)
{ {
snd_config_iterator_t i, next; 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; char *use_case_name, *file = NULL, *comment = NULL;
bool variant_ok = false; bool variant_ok = false;
int err; int err;
@ -2201,6 +2190,22 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
continue; 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 */ /* get optional use case comment */
if (strncmp(id, "Comment", 7) == 0) { if (strncmp(id, "Comment", 7) == 0) {
err = parse_string_substitute3(uc_mgr, n, &comment); 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; 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) { if (!variant) {
snd_debug(UCM, "use_case_name %s file '%s'", use_case_name, file); snd_debug(UCM, "use_case_name %s file '%s'", use_case_name, file);
/* do we have both use case name and file ? */ /* do we have both use case name and (file or config) ? */
if (!file) { if (!file && !config) {
snd_error(UCM, "use case missing file"); snd_error(UCM, "use case missing file or config");
err = -EINVAL; err = -EINVAL;
goto __error; goto __error;
} }
/* parse verb file */ /* parse verb from file or config */
err = parse_verb_file(uc_mgr, use_case_name, comment, file); 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 { } else {
/* parse variants */ /* parse variants */
struct list_head orig_variable_list; 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) if (err < 0)
break; break;
uc_mgr->parse_variant = id; uc_mgr->parse_variant = id;
err = parse_verb_file(uc_mgr, id, if (vfile || file) {
vcomment ? vcomment : comment, snd_config_t *cfg;
vfile ? vfile : file); 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; uc_mgr->parse_variant = NULL;
free(vfile); free(vfile);
free(vcomment); free(vcomment);

View file

@ -63,7 +63,7 @@ use case verbs for that sound card. i.e.:
# Example master file for blah sound card # Example master file for blah sound card
# By Joe Blogs <joe@bloggs.org> # By Joe Blogs <joe@bloggs.org>
Syntax 7 Syntax 8
# Use Case name for user interface # Use Case name for user interface
Comment "Nice Abstracted Soundcard" Comment "Nice Abstracted Soundcard"
@ -80,6 +80,31 @@ SectionUseCase."HiFi" {
Comment "Play and record HiFi quality Music." 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 # Define Value defaults
ValueDefaults { ValueDefaults {