mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-29 05:40:25 -04:00
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>
This commit is contained in:
parent
3e5a894378
commit
d7d5c724ed
6 changed files with 252 additions and 9 deletions
|
|
@ -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;
|
||||
|
|
|
|||
180
src/ucm/parser.c
180
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);
|
||||
|
|
@ -336,6 +338,159 @@ static int evaluate_define(snd_use_case_mgr_t *uc_mgr,
|
|||
return evaluate_regex(uc_mgr, cfg);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue