mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-11-04 13:30:08 -05:00
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>
This commit is contained in:
parent
25e44bbeb9
commit
c3a5d32ac3
2 changed files with 189 additions and 20 deletions
193
src/ucm/parser.c
193
src/ucm/parser.c
|
|
@ -531,8 +531,7 @@ static int evaluate_include(snd_use_case_mgr_t *uc_mgr,
|
||||||
/*
|
/*
|
||||||
* Evaluate condition (in-place)
|
* Evaluate condition (in-place)
|
||||||
*/
|
*/
|
||||||
static int evaluate_condition(snd_use_case_mgr_t *uc_mgr,
|
static int evaluate_condition(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
|
||||||
snd_config_t *cfg)
|
|
||||||
{
|
{
|
||||||
snd_config_t *n;
|
snd_config_t *n;
|
||||||
int err;
|
int err;
|
||||||
|
|
@ -548,6 +547,58 @@ static int evaluate_condition(snd_use_case_mgr_t *uc_mgr,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
* In-place evaluate
|
||||||
*/
|
*/
|
||||||
|
|
@ -555,9 +606,9 @@ int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
|
||||||
snd_config_t *cfg)
|
snd_config_t *cfg)
|
||||||
{
|
{
|
||||||
long iterations = 10000;
|
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) {
|
if (iterations == 0) {
|
||||||
uc_error("Maximal inplace evaluation iterations number reached (recursive references?)");
|
uc_error("Maximal inplace evaluation iterations number reached (recursive references?)");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
@ -575,20 +626,25 @@ int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
|
||||||
/* conditions may depend on them */
|
/* conditions may depend on them */
|
||||||
if (err2 == 0)
|
if (err2 == 0)
|
||||||
continue;
|
continue;
|
||||||
|
err3 = evaluate_variant(uc_mgr, cfg);
|
||||||
|
if (err3 < 0)
|
||||||
|
return err3;
|
||||||
|
if (err3 == 0)
|
||||||
|
continue;
|
||||||
uc_mgr->macro_hops++;
|
uc_mgr->macro_hops++;
|
||||||
if (uc_mgr->macro_hops > 100) {
|
if (uc_mgr->macro_hops > 100) {
|
||||||
uc_error("Maximal macro hops reached!");
|
uc_error("Maximal macro hops reached!");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
err3 = evaluate_macro(uc_mgr, cfg);
|
err4 = evaluate_macro(uc_mgr, cfg);
|
||||||
uc_mgr->macro_hops--;
|
uc_mgr->macro_hops--;
|
||||||
if (err3 < 0)
|
|
||||||
return err3;
|
|
||||||
if (err3 == 0)
|
|
||||||
continue;
|
|
||||||
err4 = evaluate_condition(uc_mgr, cfg);
|
|
||||||
if (err4 < 0)
|
if (err4 < 0)
|
||||||
return err3;
|
return err4;
|
||||||
|
if (err4 == 0)
|
||||||
|
continue;
|
||||||
|
err5 = evaluate_condition(uc_mgr, cfg);
|
||||||
|
if (err5 < 0)
|
||||||
|
return err5;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1998,6 +2054,65 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
* Parse master section for "Use Case" and "File" tags.
|
||||||
*/
|
*/
|
||||||
|
|
@ -2006,8 +2121,9 @@ 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;
|
snd_config_t *n, *variant = NULL;
|
||||||
char *use_case_name, *file = NULL, *comment = NULL;
|
char *use_case_name, *file = NULL, *comment = NULL;
|
||||||
|
bool variant_ok = false;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
|
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 */
|
/* in-place evaluation */
|
||||||
|
uc_mgr->parse_master_section = 1;
|
||||||
err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
|
err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
|
||||||
|
uc_mgr->parse_master_section = 0;
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto __error;
|
goto __error;
|
||||||
|
|
||||||
|
|
@ -2053,9 +2171,34 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
|
||||||
continue;
|
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_error("unknown field '%s' in SectionUseCase", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variant && !variant_ok) {
|
||||||
|
uc_error("error: undefined variant '%s'", use_case_name);
|
||||||
|
err = -EINVAL;
|
||||||
|
goto __error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!variant) {
|
||||||
uc_dbg("use_case_name %s file '%s'", use_case_name, file);
|
uc_dbg("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 ? */
|
||||||
|
|
@ -2067,6 +2210,30 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
|
||||||
|
|
||||||
/* parse verb file */
|
/* parse verb file */
|
||||||
err = parse_verb_file(uc_mgr, use_case_name, comment, 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:
|
__error:
|
||||||
free(use_case_name);
|
free(use_case_name);
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,8 @@ struct snd_use_case_mgr {
|
||||||
int conf_format;
|
int conf_format;
|
||||||
unsigned int ucm_card_number;
|
unsigned int ucm_card_number;
|
||||||
int suppress_nodev_errors;
|
int suppress_nodev_errors;
|
||||||
|
const char *parse_variant;
|
||||||
|
int parse_master_section;
|
||||||
|
|
||||||
/* UCM cards list */
|
/* UCM cards list */
|
||||||
struct list_head cards_list;
|
struct list_head cards_list;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue