2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) any later version.
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 * Support for the verb/device/modifier core logic and API,
17 * command line tool and file parser was kindly sponsored by
18 * Texas Instruments Inc.
19 * Support for multiple active modifiers and devices,
20 * transition sequences, multiple client access and user defined use
21 * cases was kindly sponsored by Wolfson Microelectronics PLC.
23 * Copyright (C) 2008-2010 SlimLogic Ltd
24 * Copyright (C) 2010 Wolfson Microelectronics PLC
25 * Copyright (C) 2010 Texas Instruments Inc.
26 * Copyright (C) 2010 Red Hat Inc.
27 * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
28 * Stefan Schmidt <stefan@slimlogic.co.uk>
29 * Justin Xu <justinx@slimlogic.co.uk>
30 * Jaroslav Kysela <perex@perex.cz>
33 #include "ucm_local.h"
40 static int filename_filter(const struct dirent64 *dirent);
42 static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
43 struct list_head *base,
47 * compose the absolute ucm filename
49 static void ucm_filename(char *fn, size_t fn_len, long version,
50 const char *dir, const char *file)
52 const char *env = getenv(version > 1 ? ALSA_CONFIG_UCM2_VAR : ALSA_CONFIG_UCM_VAR);
57 snprintf(fn, fn_len, "%s/%s/%s%s%s",
58 snd_config_topdir(), version > 1 ? "ucm2" : "ucm",
59 dir ?: "", dir ? "/" : "", file);
61 snprintf(fn, fn_len, "%s/%s%s%s",
62 env, dir ?: "", dir ? "/" : "", file);
68 int uc_mgr_config_load_file(snd_use_case_mgr_t *uc_mgr,
69 const char *file, snd_config_t **cfg, bool optional)
71 char filename[PATH_MAX];
74 ucm_filename(filename, sizeof(filename), uc_mgr->conf_format,
75 file[0] == '/' ? NULL : uc_mgr->conf_dir_name,
77 err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg, optional);
79 if (!optional || (err != -ENOENT && err != -EACCES))
80 snd_error(UCM, "failed to open file %s: %d", filename, err);
87 * Replace mallocated string
89 static char *replace_string(char **dst, const char *value)
92 *dst = value ? strdup(value) : NULL;
99 static int parse_string(snd_config_t *n, char **res)
103 err = snd_config_get_string(n, (const char **)res);
113 * Parse string and substitute
115 static int parse_string_substitute(snd_use_case_mgr_t *uc_mgr,
116 snd_config_t *n, char **res)
122 err = snd_config_get_string(n, &str);
125 err = uc_mgr_get_substituted_value(uc_mgr, &s, str);
132 * Parse string and substitute
134 static int parse_string_substitute3(snd_use_case_mgr_t *uc_mgr,
135 snd_config_t *n, char **res)
137 if (uc_mgr->conf_format < 3)
138 return parse_string(n, res);
139 return parse_string_substitute(uc_mgr, n, res);
143 * Parse integer with substitution
145 static int parse_integer_substitute(snd_use_case_mgr_t *uc_mgr,
146 snd_config_t *n, long *res)
151 err = snd_config_get_ascii(n, &s1);
154 err = uc_mgr_get_substituted_value(uc_mgr, &s2, s1);
156 err = safe_strtol(s2, res);
163 * Parse integer with substitution
165 static int parse_integer_substitute3(snd_use_case_mgr_t *uc_mgr,
166 snd_config_t *n, long *res)
171 err = snd_config_get_ascii(n, &s1);
174 if (uc_mgr->conf_format < 3)
177 err = uc_mgr_get_substituted_value(uc_mgr, &s2, s1);
179 err = safe_strtol(s2, res);
189 static int parse_is_name_safe(const char *name)
191 if (strchr(name, '.')) {
192 snd_error(UCM, "char '.' not allowed in '%s'", name);
198 static int get_string3(snd_use_case_mgr_t *uc_mgr, const char *s1, char **s)
200 if (uc_mgr->conf_format < 3) {
206 return uc_mgr_get_substituted_value(uc_mgr, s, s1);
209 static int parse_get_safe_name(snd_use_case_mgr_t *uc_mgr, snd_config_t *n,
210 const char *alt, char **name)
218 err = snd_config_get_id(n, &id);
222 err = get_string3(uc_mgr, id, name);
225 if (!parse_is_name_safe(*name)) {
233 * Parse device index from device name
234 * According to use-case.h specification, device names can have numeric suffixes
235 * like HDMI1, HDMI2, or "Line 1", "Line 2".
236 * This function extracts the index and modifies the name to contain only the base.
238 static int parse_device_index(char **name, int *index)
245 if (!name || !*name || !index)
252 /* Start from the end and find where digits begin */
255 /* Skip trailing digits */
256 while (p > *name && isdigit(*p))
259 /* If no digits found at the end, index is 0 (no index) */
260 if (p == *name + len - 1) {
265 /* Move to first digit */
268 /* Check if there's an optional space before the number */
269 if (p > *name && *(p - 1) == ' ')
272 /* Parse the index */
273 num_start = *p == ' ' ? p + 1 : p;
274 err = safe_strtol(num_start, &idx);
276 /* No valid number found */
283 /* Truncate the name to remove the index part */
285 *p = '\0'; /* Remove space and number */
287 *p = '\0'; /* Remove number only */
293 * Handle 'Error' configuration node.
295 static int error_node(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
300 err = parse_string_substitute3(uc_mgr, cfg, &s);
302 snd_error(UCM, "failed to get Error string");
305 if (!uc_mgr->suppress_nodev_errors)
306 snd_error(UCM, "%s", s);
314 static int parse_syntax_field(snd_use_case_mgr_t *uc_mgr,
315 snd_config_t *cfg, const char *filename)
321 err = snd_config_search(cfg, "Syntax", &n);
323 snd_error(UCM, "Syntax field not found in %s", filename);
326 err = snd_config_get_integer(n, &l);
328 snd_error(UCM, "Syntax field is invalid in %s", filename);
331 if (l < 2 || l > SYNTAX_VERSION_MAX) {
332 snd_error(UCM, "Incompatible syntax %ld in %s", l, filename);
335 /* delete this field to optimize strcmp() call in the parsing loop */
336 snd_config_delete(n);
337 uc_mgr->conf_format = l;
342 * Evaluate variable regex definitions (in-place delete)
344 static int evaluate_regex(snd_use_case_mgr_t *uc_mgr,
347 snd_config_iterator_t i, next;
352 err = snd_config_search(cfg, "DefineRegex", &d);
358 if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
359 snd_error(UCM, "compound type expected for DefineRegex");
363 if (uc_mgr->conf_format < 3) {
364 snd_error(UCM, "DefineRegex is supported in v3+ syntax");
368 snd_config_for_each(i, next, d) {
369 n = snd_config_iterator_entry(i);
370 err = snd_config_get_id(n, &id);
374 snd_error(UCM, "value names starting with '@' are reserved for application variables");
377 err = uc_mgr_define_regex(uc_mgr, id, n);
382 snd_config_delete(d);
387 * Evaluate variable definitions (in-place delete)
389 static int evaluate_define(snd_use_case_mgr_t *uc_mgr,
392 snd_config_iterator_t i, next;
398 err = snd_config_search(cfg, "Define", &d);
400 return evaluate_regex(uc_mgr, cfg);
404 if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
405 snd_error(UCM, "compound type expected for Define");
409 if (uc_mgr->conf_format < 3) {
410 snd_error(UCM, "Define is supported in v3+ syntax");
414 snd_config_for_each(i, next, d) {
415 n = snd_config_iterator_entry(i);
416 err = snd_config_get_id(n, &id);
419 err = snd_config_get_ascii(n, &var);
422 err = uc_mgr_get_substituted_value(uc_mgr, &s, var);
428 snd_error(UCM, "value names starting with '@' are reserved for application variables");
432 if (uc_mgr->conf_format >= 9) {
433 err = uc_mgr_get_substituted_value(uc_mgr, &sid, id);
439 err = uc_mgr_set_variable(uc_mgr, sid, s);
447 snd_config_delete(d);
449 return evaluate_regex(uc_mgr, cfg);
453 * Evaluate macro definitions (in-place delete)
455 static int evaluate_define_macro(snd_use_case_mgr_t *uc_mgr,
461 err = snd_config_search(cfg, "DefineMacro", &d);
467 if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
468 snd_error(UCM, "compound type expected for DefineMacro");
472 if (uc_mgr->conf_format < 6) {
473 snd_error(UCM, "DefineMacro is supported in v6+ syntax");
477 err = snd_config_merge(uc_mgr->macros, d, 0);
484 static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr,
488 snd_config_iterator_t i, next;
489 snd_config_t *m, *mc, *a, *n;
490 const char *mid, *id;
491 char name[128], *var, *var2;
495 err = snd_config_get_id(args, &mid);
498 err = snd_config_search(uc_mgr->macros, mid, &m);
500 snd_error(UCM, "Macro '%s' is not defined", mid);
505 if (snd_config_get_type(args) == SND_CONFIG_TYPE_STRING) {
506 err = snd_config_get_string(args, &s);
509 if (uc_mgr->conf_format < 9) {
510 err = snd_config_load_string(&a, s, 0);
512 err = uc_mgr_get_substituted_value(uc_mgr, &var2, s);
514 err = snd_config_load_string(&a, var2, 0);
520 } else if (snd_config_get_type(args) != SND_CONFIG_TYPE_COMPOUND) {
525 snd_config_for_each(i, next, a) {
526 n = snd_config_iterator_entry(i);
527 err = snd_config_get_id(n, &id);
530 snprintf(name, sizeof(name), "__%s", id);
531 if (uc_mgr_get_variable(uc_mgr, name, false)) {
532 snd_error(UCM, "Macro argument '%s' is already defined", name);
535 err = snd_config_get_ascii(n, &var);
538 if (uc_mgr->conf_format < 7) {
539 err = uc_mgr_set_variable(uc_mgr, name, var);
542 err = uc_mgr_get_substituted_value(uc_mgr, &var2, var);
545 err = uc_mgr_set_variable(uc_mgr, name, var2);
553 /* merge + substitute variables */
554 err = snd_config_copy(&mc, m);
557 err = uc_mgr_evaluate_inplace(uc_mgr, mc);
559 snd_config_delete(mc);
562 err = uc_mgr_config_tree_merge(uc_mgr, dst, mc, NULL, NULL);
563 snd_config_delete(mc);
565 /* delete arguments */
566 snd_config_for_each(i, next, a) {
567 n = snd_config_iterator_entry(i);
568 err = snd_config_get_id(n, &id);
571 snprintf(name, sizeof(name), "__%s", id);
572 err = uc_mgr_delete_variable(uc_mgr, name);
579 snd_config_delete(a);
584 * Evaluate macro definitions and instances (in-place delete)
586 static int evaluate_macro(snd_use_case_mgr_t *uc_mgr,
589 snd_config_iterator_t i, i2, next, next2;
590 snd_config_t *d, *n, *n2;
593 ret = evaluate_define_macro(uc_mgr, cfg);
597 err = snd_config_search(cfg, "Macro", &d);
603 if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
604 snd_error(UCM, "compound type expected for DefineMacro");
608 if (uc_mgr->conf_format < 6) {
609 snd_error(UCM, "Macro is supported in v6+ syntax");
613 snd_config_for_each(i, next, d) {
614 n = snd_config_iterator_entry(i);
615 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
617 if (snd_config_get_id(n, &id))
619 snd_error(UCM, "compound type expected for Macro.%s", id);
622 snd_config_for_each(i2, next2, n) {
623 n2 = snd_config_iterator_entry(i2);
624 err = evaluate_macro1(uc_mgr, cfg, n2);
630 snd_config_delete(d);
636 * Evaluate include (in-place)
638 static int evaluate_include(snd_use_case_mgr_t *uc_mgr,
644 err = snd_config_search(cfg, "Include", &n);
650 err = uc_mgr_evaluate_include(uc_mgr, cfg, n);
651 snd_config_delete(n);
656 * Evaluate condition (in-place)
658 static int evaluate_condition(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
663 err = snd_config_search(cfg, "If", &n);
669 err = uc_mgr_evaluate_condition(uc_mgr, cfg, n);
670 snd_config_delete(n);
675 * Evaluate variant (in-place)
677 static int evaluate_variant(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
679 snd_config_iterator_t i, next;
684 err = snd_config_search(cfg, "Variant", &c);
690 if (uc_mgr->conf_format < 6) {
691 snd_error(UCM, "Variant is supported in v6+ syntax");
695 if (uc_mgr->parse_master_section)
698 if (uc_mgr->parse_variant == NULL)
701 snd_config_for_each(i, next, c) {
702 n = snd_config_iterator_entry(i);
704 if (snd_config_get_id(n, &id) < 0)
707 if (strcmp(id, uc_mgr->parse_variant))
710 err = uc_mgr_evaluate_inplace(uc_mgr, n);
714 err = uc_mgr_config_tree_merge(uc_mgr, cfg, n, NULL, NULL);
717 snd_config_delete(c);
722 snd_config_delete(c);
729 int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
732 long iterations = 10000;
733 int err1 = 0, err2 = 0, err3 = 0, err4 = 0, err5 = 0, err6 = 0;
735 while (err1 == 0 || err2 == 0 || err3 == 0 || err4 == 0 || err5 == 0 || err6 == 0) {
736 if (iterations == 0) {
737 snd_error(UCM, "Maximal inplace evaluation iterations number reached (recursive references?)");
741 /* variables at first */
742 err1 = evaluate_define(uc_mgr, cfg);
745 /* include at second */
746 err2 = evaluate_include(uc_mgr, cfg);
749 /* include or macro may define another variables */
750 /* conditions may depend on them */
753 err3 = evaluate_variant(uc_mgr, cfg);
758 uc_mgr->macro_hops++;
759 if (uc_mgr->macro_hops > 100) {
760 snd_error(UCM, "Maximal macro hops reached!");
763 err4 = evaluate_macro(uc_mgr, cfg);
764 uc_mgr->macro_hops--;
769 err5 = uc_mgr_evaluate_repeat(uc_mgr, cfg);
772 err6 = evaluate_condition(uc_mgr, cfg);
780 * Parse one item for alsa-lib config
782 static int parse_libconfig1(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
784 snd_config_iterator_t i, next;
785 snd_config_t *n, *config = NULL;
786 const char *id, *file = NULL;
787 bool substfile = false, substconfig = false;
790 if (snd_config_get_id(cfg, &id) < 0)
793 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
794 snd_error(UCM, "compound type expected for %s", id);
798 snd_config_for_each(i, next, cfg) {
799 n = snd_config_iterator_entry(i);
801 if (snd_config_get_id(n, &id) < 0)
804 if (strcmp(id, "File") == 0 ||
805 strcmp(id, "SubstiFile") == 0) {
806 substfile = id[0] == 'S';
807 err = snd_config_get_string(n, &file);
813 if (strcmp(id, "Config") == 0 ||
814 strcmp(id, "SubstiConfig") == 0) {
815 substconfig = id[0] == 'S';
816 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND)
822 snd_error(UCM, "unknown field %s", id);
829 err = uc_mgr_config_load_file(uc_mgr, file, &cfg, false);
832 err = uc_mgr_substitute_tree(uc_mgr, cfg);
834 snd_config_delete(cfg);
837 err = snd_config_merge(uc_mgr->local_config, cfg, 0);
839 snd_config_delete(cfg);
843 char filename[PATH_MAX];
845 ucm_filename(filename, sizeof(filename), uc_mgr->conf_format,
846 file[0] == '/' ? NULL : uc_mgr->conf_dir_name,
848 err = uc_mgr_config_load_into(uc_mgr->conf_format, filename, uc_mgr->local_config, false);
856 err = uc_mgr_substitute_tree(uc_mgr, config);
860 err = snd_config_merge(uc_mgr->local_config, config, 0);
869 * Parse alsa-lib config
871 static int parse_libconfig(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
873 snd_config_iterator_t i, next;
878 if (snd_config_get_id(cfg, &id) < 0)
881 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
882 snd_error(UCM, "compound type expected for %s", id);
886 snd_config_for_each(i, next, cfg) {
887 n = snd_config_iterator_entry(i);
889 err = parse_libconfig1(uc_mgr, n);
900 static int parse_transition(snd_use_case_mgr_t *uc_mgr,
901 struct list_head *tlist,
904 struct transition_sequence *tseq;
906 snd_config_iterator_t i, next;
910 if (snd_config_get_id(cfg, &id) < 0)
913 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
914 snd_error(UCM, "compound type expected for %s", id);
918 snd_config_for_each(i, next, cfg) {
919 n = snd_config_iterator_entry(i);
921 if (snd_config_get_id(n, &id) < 0)
924 tseq = calloc(1, sizeof(*tseq));
927 INIT_LIST_HEAD(&tseq->transition_list);
929 err = get_string3(uc_mgr, id, &tseq->name);
935 err = parse_sequence(uc_mgr, &tseq->transition_list, n);
937 uc_mgr_free_transition_element(tseq);
941 list_add(&tseq->list, tlist);
949 static int parse_compound(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
950 int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
951 void *data1, void *data2)
954 snd_config_iterator_t i, next;
958 if (snd_config_get_id(cfg, &id) < 0)
961 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
962 snd_error(UCM, "compound type expected for %s", id);
966 snd_config_for_each(i, next, cfg) {
967 n = snd_config_iterator_entry(i);
969 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
970 snd_error(UCM, "compound type expected for %s, is %d", id, snd_config_get_type(cfg));
974 err = fcn(uc_mgr, n, data1, data2);
982 static int strip_legacy_dev_index(char *name)
984 char *dot = strchr(name, '.');
987 if (dot[1] != '0' || dot[2] != '\0') {
988 snd_error(UCM, "device name %s contains a '.',"
989 " and is not legacy foo.0 format", name);
1000 static int parse_device_list(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
1001 struct dev_list *dev_list,
1002 enum dev_list_type type,
1005 struct dev_list_node *sdev;
1007 snd_config_iterator_t i, next;
1011 if (dev_list->type != DEVLIST_NONE) {
1012 snd_error(UCM, "multiple supported or conflicting device lists");
1017 if (snd_config_get_id(cfg, &id) < 0)
1020 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1021 snd_error(UCM, "compound type expected for %s", id);
1025 snd_config_for_each(i, next, cfg) {
1026 n = snd_config_iterator_entry(i);
1028 if (snd_config_get_id(n, &id) < 0)
1031 sdev = calloc(1, sizeof(struct dev_list_node));
1034 err = parse_string_substitute3(uc_mgr, n, &sdev->name);
1039 err = strip_legacy_dev_index(sdev->name);
1045 list_add(&sdev->list, &dev_list->list);
1048 dev_list->type = type;
1053 /* Find a component device by its name, and remove it from machine device
1056 * Component devices are defined by machine components (usually off-soc
1057 * codes or DSP embeded in SoC). Since alsaconf imports their configuration
1058 * files automatically, we don't know which devices are component devices
1059 * until they are referenced by a machine device sequence. So here when we
1060 * find a referenced device, we move it from the machine device list to the
1061 * component device list. Component devices will not be exposed to applications
1062 * by the original API to list devices for backward compatibility. So sound
1063 * servers can only see the machine devices.
1065 struct use_case_device *find_component_dev(snd_use_case_mgr_t *uc_mgr,
1068 struct list_head *pos, *posdev, *_posdev;
1069 struct use_case_verb *verb;
1070 struct use_case_device *dev;
1072 list_for_each(pos, &uc_mgr->verb_list) {
1073 verb = list_entry(pos, struct use_case_verb, list);
1075 /* search in the component device list */
1076 list_for_each(posdev, &verb->cmpt_device_list) {
1077 dev = list_entry(posdev, struct use_case_device, list);
1078 if (!strcmp(dev->name, name))
1082 /* search the machine device list */
1083 list_for_each_safe(posdev, _posdev, &verb->device_list) {
1084 dev = list_entry(posdev, struct use_case_device, list);
1085 if (!strcmp(dev->name, name)) {
1086 /* find the component device, move it from the
1087 * machine device list to the component device
1090 list_del(&dev->list);
1091 list_add_tail(&dev->list,
1092 &verb->cmpt_device_list);
1101 /* parse sequence of a component device
1103 * This function will find the component device and mark if its enable or
1104 * disable sequence is needed by its parenet device.
1106 static int parse_component_seq(snd_use_case_mgr_t *uc_mgr,
1107 snd_config_t *n, int enable,
1108 struct component_sequence *cmpt_seq)
1113 err = parse_string_substitute3(uc_mgr, n, &val);
1117 cmpt_seq->device = find_component_dev(uc_mgr, val);
1118 if (!cmpt_seq->device) {
1119 snd_error(UCM, "Cannot find component device %s", val);
1125 /* Parent needs its enable or disable sequence */
1126 cmpt_seq->enable = enable;
1134 * Sequence controls elements are in the following form:-
1137 * cset "element_id_syntax value_syntax"
1139 * exec "any unix command with arguments"
1140 * enadev "component device name"
1141 * disdev "component device name"
1144 * cset "name='Master Playback Switch' 0,0"
1145 * cset "iface=PCM,name='Disable HDMI',index=1 0"
1146 * enadev "rt286:Headphones"
1147 * disdev "rt286:Speaker"
1149 static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
1150 struct list_head *base,
1153 struct sequence_element *curr;
1154 snd_config_iterator_t i, next;
1157 const char *cmd = NULL;
1159 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1160 snd_error(UCM, "compound is expected for sequence definition");
1164 snd_config_for_each(i, next, cfg) {
1167 n = snd_config_iterator_entry(i);
1168 err = snd_config_get_id(n, &id);
1172 if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
1173 snd_error(UCM, "string type is expected for sequence command");
1176 err = snd_config_get_string(n, &cmd);
1182 /* alloc new sequence element */
1183 curr = calloc(1, sizeof(struct sequence_element));
1186 list_add_tail(&curr->list, base);
1188 if (strcmp(cmd, "cdev") == 0) {
1189 curr->type = SEQUENCE_ELEMENT_TYPE_CDEV;
1190 err = parse_string_substitute3(uc_mgr, n, &curr->data.cdev);
1192 snd_error(UCM, "cdev requires a string!");
1198 if (strcmp(cmd, "cset") == 0) {
1199 curr->type = SEQUENCE_ELEMENT_TYPE_CSET;
1201 err = parse_string_substitute3(uc_mgr, n, &curr->data.cset);
1203 snd_error(UCM, "%s requires a string!", cmd);
1209 if (strcmp(cmd, "enadev") == 0 ||
1210 strcmp(cmd, "disdev") == 0) {
1211 /* need to enable or disable a component device */
1212 curr->type = SEQUENCE_ELEMENT_TYPE_CMPT_SEQ;
1213 err = parse_component_seq(uc_mgr, n,
1214 strcmp(cmd, "enadev") == 0,
1215 &curr->data.cmpt_seq);
1217 snd_error(UCM, "%s requires a valid device!", cmd);
1223 if (strcmp(cmd, "enadev2") == 0) {
1224 curr->type = SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ;
1228 if (strcmp(cmd, "disdev2") == 0) {
1229 curr->type = SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_SEQ;
1231 err = parse_string_substitute3(uc_mgr, n, &curr->data.device);
1233 snd_error(UCM, "%s requires a valid device!", cmd);
1239 if (strcmp(cmd, "disdevall") == 0) {
1240 curr->type = SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_ALL;
1244 if (strcmp(cmd, "cset-bin-file") == 0) {
1245 curr->type = SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE;
1249 if (strcmp(cmd, "cset-tlv") == 0) {
1250 curr->type = SEQUENCE_ELEMENT_TYPE_CSET_TLV;
1254 if (strcmp(cmd, "cset-new") == 0) {
1255 curr->type = SEQUENCE_ELEMENT_TYPE_CSET_NEW;
1259 if (strcmp(cmd, "ctl-remove") == 0) {
1260 curr->type = SEQUENCE_ELEMENT_TYPE_CTL_REMOVE;
1264 if (strcmp(cmd, "sysw") == 0) {
1265 curr->type = SEQUENCE_ELEMENT_TYPE_SYSSET;
1266 err = parse_string_substitute3(uc_mgr, n, &curr->data.sysw);
1268 snd_error(UCM, "sysw requires a string!");
1274 if (strcmp(cmd, "usleep") == 0) {
1275 curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
1276 err = parse_integer_substitute3(uc_mgr, n, &curr->data.sleep);
1278 snd_error(UCM, "usleep requires integer!");
1284 if (strcmp(cmd, "msleep") == 0) {
1285 curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
1286 err = parse_integer_substitute3(uc_mgr, n, &curr->data.sleep);
1288 snd_error(UCM, "msleep requires integer!");
1291 curr->data.sleep *= 1000L;
1295 if (strcmp(cmd, "exec") == 0) {
1296 curr->type = SEQUENCE_ELEMENT_TYPE_EXEC;
1298 err = parse_string_substitute3(uc_mgr, n, &curr->data.exec);
1300 snd_error(UCM, "exec requires a string!");
1306 if (strcmp(cmd, "shell") == 0) {
1307 curr->type = SEQUENCE_ELEMENT_TYPE_SHELL;
1311 if (strcmp(cmd, "cfg-save") == 0) {
1312 curr->type = SEQUENCE_ELEMENT_TYPE_CFGSAVE;
1313 err = parse_string_substitute3(uc_mgr, n, &curr->data.cfgsave);
1315 snd_error(UCM, "sysw requires a string!");
1321 if (strcmp(cmd, "comment") == 0)
1324 snd_error(UCM, "sequence command '%s' is ignored", cmd);
1327 list_del(&curr->list);
1328 uc_mgr_free_sequence_element(curr);
1337 int uc_mgr_add_value(struct list_head *base, const char *key, char *val)
1339 struct ucm_value *curr;
1341 curr = calloc(1, sizeof(struct ucm_value));
1344 curr->name = strdup(key);
1345 if (curr->name == NULL) {
1349 list_add_tail(&curr->list, base);
1357 * Parse values describing PCM, control/mixer settings and stream parameters.
1362 * PlaybackVolume "name='Master Playback Volume',index=2"
1363 * PlaybackSwitch "name='Master Playback Switch',index=2"
1366 static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
1367 struct list_head *base,
1370 snd_config_iterator_t i, next;
1373 snd_config_type_t type;
1376 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1377 snd_error(UCM, "compound is expected for value definition");
1381 /* in-place evaluation */
1382 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1386 snd_config_for_each(i, next, cfg) {
1388 n = snd_config_iterator_entry(i);
1389 err = snd_config_get_id(n, &id);
1393 type = snd_config_get_type(n);
1395 case SND_CONFIG_TYPE_INTEGER:
1396 case SND_CONFIG_TYPE_INTEGER64:
1397 case SND_CONFIG_TYPE_REAL:
1398 err = snd_config_get_ascii(n, &s);
1400 snd_error(UCM, "unable to parse value for id '%s': %s!", id, snd_strerror(err));
1404 case SND_CONFIG_TYPE_STRING:
1405 err = parse_string_substitute(uc_mgr, n, &s);
1407 snd_error(UCM, "unable to parse a string for id '%s'!", id);
1412 snd_error(UCM, "invalid type %i in Value compound '%s'", type, id);
1415 err = uc_mgr_add_value(base, id, s);
1426 * Parse Modifier Use cases
1428 * # Each modifier is described in new section. N modifiers are allowed
1429 * SectionModifier."Capture Voice" {
1431 * Comment "Record voice call"
1438 * ConflictingDevice [
1451 * TransitionSequence."ToModifierName" [
1455 * # Optional TQ and ALSA PCMs
1459 * PlaybackVolume "name='Master Playback Volume',index=2"
1460 * PlaybackSwitch "name='Master Playback Switch',index=2"
1464 * SupportedDevice and ConflictingDevice cannot be specified together.
1465 * Both are optional.
1467 static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
1469 void *data1, void *data2)
1471 struct use_case_verb *verb = data1;
1472 struct use_case_modifier *modifier;
1474 snd_config_iterator_t i, next;
1478 if (parse_get_safe_name(uc_mgr, cfg, data2, &name) < 0)
1481 /* allocate modifier */
1482 modifier = calloc(1, sizeof(*modifier));
1483 if (modifier == NULL) {
1487 INIT_LIST_HEAD(&modifier->enable_list);
1488 INIT_LIST_HEAD(&modifier->disable_list);
1489 INIT_LIST_HEAD(&modifier->transition_list);
1490 INIT_LIST_HEAD(&modifier->dev_list.list);
1491 INIT_LIST_HEAD(&modifier->value_list);
1492 list_add_tail(&modifier->list, &verb->modifier_list);
1493 modifier->name = name;
1495 /* in-place evaluation */
1496 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1500 snd_config_for_each(i, next, cfg) {
1502 n = snd_config_iterator_entry(i);
1503 if (snd_config_get_id(n, &id) < 0)
1506 if (strcmp(id, "Comment") == 0) {
1507 err = parse_string_substitute3(uc_mgr, n, &modifier->comment);
1509 snd_error(UCM, "failed to get modifier comment");
1515 if (strcmp(id, "SupportedDevice") == 0) {
1516 err = parse_device_list(uc_mgr, &modifier->dev_list,
1517 DEVLIST_SUPPORTED, n);
1519 snd_error(UCM, "failed to parse supported device list");
1524 if (strcmp(id, "ConflictingDevice") == 0) {
1525 err = parse_device_list(uc_mgr, &modifier->dev_list,
1526 DEVLIST_CONFLICTING, n);
1528 snd_error(UCM, "failed to parse conflicting device list");
1533 if (strcmp(id, "EnableSequence") == 0) {
1534 err = parse_sequence(uc_mgr, &modifier->enable_list, n);
1536 snd_error(UCM, "failed to parse modifier enable sequence");
1542 if (strcmp(id, "DisableSequence") == 0) {
1543 err = parse_sequence(uc_mgr, &modifier->disable_list, n);
1545 snd_error(UCM, "failed to parse modifier disable sequence");
1551 if (strcmp(id, "TransitionSequence") == 0) {
1552 err = parse_transition(uc_mgr, &modifier->transition_list, n);
1554 snd_error(UCM, "failed to parse transition modifier");
1560 if (strcmp(id, "Value") == 0) {
1561 err = parse_value(uc_mgr, &modifier->value_list, n);
1563 snd_error(UCM, "failed to parse Value");
1574 * Parse device configuration fields
1576 static int parse_device_fields(snd_use_case_mgr_t *uc_mgr,
1578 struct use_case_device *device)
1580 snd_config_iterator_t i, next;
1584 snd_config_for_each(i, next, cfg) {
1586 n = snd_config_iterator_entry(i);
1587 if (snd_config_get_id(n, &id) < 0)
1590 if (strcmp(id, "Comment") == 0) {
1591 err = parse_string_substitute3(uc_mgr, n, &device->comment);
1593 snd_error(UCM, "failed to get device comment");
1599 if (strcmp(id, "SupportedDevice") == 0) {
1600 err = parse_device_list(uc_mgr, &device->dev_list,
1601 DEVLIST_SUPPORTED, n);
1603 snd_error(UCM, "failed to parse supported device list");
1609 if (strcmp(id, "ConflictingDevice") == 0) {
1610 err = parse_device_list(uc_mgr, &device->dev_list,
1611 DEVLIST_CONFLICTING, n);
1613 snd_error(UCM, "failed to parse conflicting device list");
1619 if (strcmp(id, "EnableSequence") == 0) {
1620 err = parse_sequence(uc_mgr, &device->enable_list, n);
1622 snd_error(UCM, "failed to parse device enable sequence");
1629 if (strcmp(id, "DisableSequence") == 0) {
1630 err = parse_sequence(uc_mgr, &device->disable_list, n);
1632 snd_error(UCM, "failed to parse device disable sequence");
1639 if (strcmp(id, "TransitionSequence") == 0) {
1640 err = parse_transition(uc_mgr, &device->transition_list, n);
1642 snd_error(UCM, "failed to parse transition device");
1649 if (strcmp(id, "Value") == 0) {
1650 err = parse_value(uc_mgr, &device->value_list, n);
1652 snd_error(UCM, "failed to parse Value");
1662 * Helper function to copy, evaluate and optionally merge configuration trees.
1664 static int uc_mgr_config_copy_eval_merge(snd_use_case_mgr_t *uc_mgr,
1667 snd_config_t *merge_from)
1669 snd_config_t *tmp = NULL;
1672 err = snd_config_copy(&tmp, src);
1676 err = uc_mgr_evaluate_inplace(uc_mgr, tmp);
1678 snd_config_delete(tmp);
1683 err = uc_mgr_config_tree_merge(uc_mgr, tmp, merge_from, NULL, NULL);
1685 snd_config_delete(tmp);
1695 * Parse Device Use Cases
1697 * # Each device is described in new section. N devices are allowed
1698 * SectionDevice."Headphones" {
1699 * Comment "Headphones connected to 3.5mm jack"
1706 * ConflictingDevice [
1719 * TransitionSequence."ToDevice" [
1724 * PlaybackVolume "name='Master Playback Volume',index=2"
1725 * PlaybackSwitch "name='Master Playback Switch',index=2"
1730 static int parse_device_by_name(snd_use_case_mgr_t *uc_mgr,
1732 struct use_case_verb *verb,
1734 struct use_case_device **ret_device)
1736 struct use_case_device *device;
1739 device = calloc(1, sizeof(*device));
1743 INIT_LIST_HEAD(&device->enable_list);
1744 INIT_LIST_HEAD(&device->disable_list);
1745 INIT_LIST_HEAD(&device->transition_list);
1746 INIT_LIST_HEAD(&device->dev_list.list);
1747 INIT_LIST_HEAD(&device->value_list);
1748 INIT_LIST_HEAD(&device->variants);
1749 INIT_LIST_HEAD(&device->variant_list);
1750 list_add_tail(&device->list, &verb->device_list);
1751 device->name = strdup(name);
1752 if (device->name == NULL) {
1756 device->orig_name = strdup(name);
1757 if (device->orig_name == NULL)
1760 err = parse_device_fields(uc_mgr, cfg, device);
1765 *ret_device = device;
1770 static int parse_device(snd_use_case_mgr_t *uc_mgr,
1772 void *data1, void *data2)
1774 struct use_case_verb *verb = data1;
1776 const char *variant_label = NULL;
1777 struct use_case_device *device = NULL;
1778 snd_config_t *primary_cfg_copy = NULL;
1779 snd_config_t *device_variant = NULL;
1780 snd_config_t *merged_cfg = NULL;
1781 snd_config_iterator_t i, next;
1785 if (parse_get_safe_name(uc_mgr, cfg, data2, &name) < 0)
1788 if (uc_mgr->conf_format >= 8 && (colon = strchr(name, ':'))) {
1789 variant_label = colon + 1;
1791 err = snd_config_search(cfg, "DeviceVariant", &device_variant);
1793 snd_config_t *variant_cfg = NULL;
1795 /* Save a copy of the primary config for creating variant devices */
1796 err = snd_config_copy(&primary_cfg_copy, cfg);
1802 err = snd_config_search(device_variant, variant_label, &variant_cfg);
1804 err = uc_mgr_config_copy_eval_merge(uc_mgr, &merged_cfg, cfg, variant_cfg);
1814 /* in-place evaluation */
1815 if (cfg != merged_cfg) {
1816 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1823 err = parse_device_by_name(uc_mgr, cfg, verb, name, &device);
1829 snd_config_delete(merged_cfg);
1833 if (device_variant == NULL)
1836 if (device->dev_list.type == DEVLIST_SUPPORTED) {
1837 snd_error(UCM, "DeviceVariant cannot be used with SupportedDevice");
1842 if (snd_config_get_type(device_variant) != SND_CONFIG_TYPE_COMPOUND) {
1843 snd_error(UCM, "compound type expected for DeviceVariant");
1848 colon = strchr(device->name, ':');
1850 snd_error(UCM, "DeviceVariant requires ':' in device name");
1855 snd_config_for_each(i, next, device_variant) {
1856 const char *variant_name;
1857 char variant_device_name[128];
1858 struct use_case_device *variant = NULL;
1860 n = snd_config_iterator_entry(i);
1862 if (snd_config_get_id(n, &variant_name) < 0)
1865 /* Create variant device name: base:variant_name */
1866 snprintf(variant_device_name, sizeof(variant_device_name),
1867 "%.*s:%s", (int)(colon - device->name),
1868 device->name, variant_name);
1870 err = uc_mgr_config_copy_eval_merge(uc_mgr, &merged_cfg, primary_cfg_copy, n);
1874 err = parse_device_by_name(uc_mgr, merged_cfg, verb,
1875 variant_device_name, &variant);
1876 snd_config_delete(merged_cfg);
1881 /* Link variant to primary device */
1882 list_add(&variant->variant_list, &device->variants);
1884 err = uc_mgr_put_to_dev_list(&device->dev_list, variant->name);
1887 if (device->dev_list.type == DEVLIST_NONE)
1888 device->dev_list.type = DEVLIST_CONFLICTING;
1895 snd_config_delete(merged_cfg);
1896 if (primary_cfg_copy)
1897 snd_config_delete(primary_cfg_copy);
1902 * Parse Device Rename/Delete Command
1904 * # The devices might be renamed to allow the better conditional runtime
1905 * # evaluation. Bellow example renames Speaker1 device to Speaker and
1906 * # removes Speaker2 device.
1907 * RenameDevice."Speaker1" "Speaker"
1908 * RemoveDevice."Speaker2" "Speaker2"
1910 static int parse_dev_name_list(snd_use_case_mgr_t *uc_mgr,
1912 struct list_head *list)
1915 snd_config_iterator_t i, next;
1916 const char *id, *name1;
1917 char *name1s, *name2;
1918 struct ucm_dev_name *dev;
1919 snd_config_iterator_t pos;
1922 if (snd_config_get_id(cfg, &id) < 0)
1925 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1926 snd_error(UCM, "compound type expected for %s", id);
1930 snd_config_for_each(i, next, cfg) {
1931 n = snd_config_iterator_entry(i);
1933 if (snd_config_get_id(n, &name1) < 0)
1936 err = get_string3(uc_mgr, name1, &name1s);
1940 err = parse_string_substitute3(uc_mgr, n, &name2);
1943 snd_error(UCM, "failed to get target device name for '%s'", name1);
1947 /* skip duplicates */
1948 list_for_each(pos, list) {
1949 dev = list_entry(pos, struct ucm_dev_name, list);
1950 if (strcmp(dev->name1, name1s) == 0) {
1959 dev = calloc(1, sizeof(*dev));
1964 dev->name1 = strdup(name1);
1965 if (dev->name1 == NULL) {
1971 list_add_tail(&dev->list, list);
1977 static int parse_compound_check_legacy(snd_use_case_mgr_t *uc_mgr,
1979 int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
1982 const char *id, *idchild;
1983 int child_ctr = 0, legacy_format = 1;
1984 snd_config_iterator_t i, next;
1985 snd_config_t *child;
1988 err = snd_config_get_id(cfg, &id);
1992 snd_config_for_each(i, next, cfg) {
1994 if (child_ctr > 1) {
1998 child = snd_config_iterator_entry(i);
2000 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
2005 if (snd_config_get_id(child, &idchild) < 0)
2008 if (strcmp(idchild, "0")) {
2013 if (child_ctr != 1) {
2018 return parse_compound(uc_mgr, cfg, fcn, data1, (void *)id);
2020 return fcn(uc_mgr, cfg, data1, NULL);
2023 static int parse_device_name(snd_use_case_mgr_t *uc_mgr,
2026 void *data2 ATTRIBUTE_UNUSED)
2028 return parse_compound_check_legacy(uc_mgr, cfg, parse_device, data1);
2031 static int parse_modifier_name(snd_use_case_mgr_t *uc_mgr,
2034 void *data2 ATTRIBUTE_UNUSED)
2036 return parse_compound_check_legacy(uc_mgr, cfg, parse_modifier, data1);
2039 static int verb_dev_list_add(struct use_case_verb *verb,
2040 enum dev_list_type dst_type,
2044 struct use_case_device *device;
2045 struct list_head *pos;
2047 list_for_each(pos, &verb->device_list) {
2048 device = list_entry(pos, struct use_case_device, list);
2050 if (strcmp(device->name, dst) != 0)
2052 if (device->dev_list.type != dst_type) {
2053 if (list_empty(&device->dev_list.list)) {
2054 device->dev_list.type = dst_type;
2056 snd_error(UCM, "incompatible device list type ('%s', '%s')", device->orig_name, src);
2060 return uc_mgr_put_to_dev_list(&device->dev_list, src);
2062 snd_error(UCM, "unable to find device '%s'", dst);
2066 static int verb_dev_list_check(struct use_case_verb *verb)
2068 struct list_head *pos, *pos2, *pos3;
2069 struct use_case_device *device, *target_dev;
2070 struct dev_list_node *dlist, *dlist2;
2072 int max_iterations = 100; /* safety limit to prevent infinite loops */
2073 bool added_something;
2075 /* First pass: ensure bidirectional relationships */
2076 list_for_each(pos, &verb->device_list) {
2077 device = list_entry(pos, struct use_case_device, list);
2078 list_for_each(pos2, &device->dev_list.list) {
2079 dlist = list_entry(pos2, struct dev_list_node, list);
2080 err = verb_dev_list_add(verb, device->dev_list.type,
2081 dlist->name, device->name);
2087 /* Second pass: complete other relationships for devices in group.
2088 * For n devices, at most n-1 iterations are needed.
2090 for (iteration = 0; iteration < max_iterations; iteration++) {
2091 added_something = false;
2093 list_for_each(pos, &verb->device_list) {
2094 device = list_entry(pos, struct use_case_device, list);
2096 if (device->dev_list.type == DEVLIST_NONE)
2099 list_for_each(pos2, &device->dev_list.list) {
2100 dlist = list_entry(pos2, struct dev_list_node, list);
2103 list_for_each(pos3, &verb->device_list) {
2104 struct use_case_device *tmp_dev;
2105 tmp_dev = list_entry(pos3, struct use_case_device, list);
2106 if (strcmp(tmp_dev->name, dlist->name) == 0) {
2107 target_dev = tmp_dev;
2115 list_for_each(pos3, &device->dev_list.list) {
2116 dlist2 = list_entry(pos3, struct dev_list_node, list);
2118 if (strcmp(dlist2->name, target_dev->name) == 0)
2121 /* verb_dev_list_add returns 1 if device was added, 0 if already exists */
2122 err = verb_dev_list_add(verb, device->dev_list.type,
2123 target_dev->name, dlist2->name);
2127 added_something = true;
2132 /* If nothing was added in this iteration, we're done */
2133 if (!added_something)
2137 if (iteration >= max_iterations) {
2138 snd_error(UCM, "too many device list iterations for verb '%s'", verb->name);
2146 * Check if a device name is already in use
2148 static int is_device_name_used(struct use_case_verb *verb, const char *name, struct use_case_device *current)
2150 struct list_head *pos;
2151 struct use_case_device *device;
2153 list_for_each(pos, &verb->device_list) {
2154 device = list_entry(pos, struct use_case_device, list);
2155 if (device != current && strcmp(device->name, name) == 0)
2162 * Update all references to a device name in modifiers and other devices.
2163 * This helper function is used when renaming devices to ensure all
2164 * dev_list references are updated accordingly.
2166 static int verb_update_device_references(struct use_case_verb *verb,
2167 const char *old_name,
2168 const char *new_name)
2170 struct list_head *pos, *pos2;
2171 struct use_case_device *device;
2172 struct use_case_modifier *modifier;
2173 struct dev_list_node *dlist;
2176 list_for_each(pos, &verb->modifier_list) {
2177 modifier = list_entry(pos, struct use_case_modifier, list);
2178 list_for_each(pos2, &modifier->dev_list.list) {
2179 dlist = list_entry(pos2, struct dev_list_node, list);
2180 if (strcmp(dlist->name, old_name) == 0) {
2181 name_copy = strdup(new_name);
2182 if (name_copy == NULL)
2185 dlist->name = name_copy;
2190 list_for_each(pos, &verb->device_list) {
2191 device = list_entry(pos, struct use_case_device, list);
2192 list_for_each(pos2, &device->dev_list.list) {
2193 dlist = list_entry(pos2, struct dev_list_node, list);
2194 if (strcmp(dlist->name, old_name) == 0) {
2195 name_copy = strdup(new_name);
2196 if (name_copy == NULL)
2199 dlist->name = name_copy;
2208 * Normalize device names according to use-case.h specification.
2209 * Device names like "HDMI 1" or "Line 1" should be normalized to "HDMI1" and "Line1".
2210 * When device name contains ':', add index and remove everything after ':' (including).
2211 * If final name is already used, retry with higher index.
2212 * Also updates dev_list members in modifiers and devices to reference the normalized names.
2214 static int verb_normalize_device_names(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb)
2216 struct list_head *pos;
2217 struct use_case_device *device;
2218 char *orig_name, *norm_name, *colon;
2222 list_for_each(pos, &verb->device_list) {
2223 device = list_entry(pos, struct use_case_device, list);
2225 orig_name = strdup(device->name);
2226 if (orig_name == NULL)
2229 norm_name = strdup(device->name);
2230 if (norm_name == NULL) {
2235 if (uc_mgr->conf_format < 8)
2238 colon = strchr(norm_name, ':');
2240 if (colon[1] == '\0' || strchr(colon + 1, ' ')) {
2241 snd_error(UCM, "device descriptor cannot be empty or contain spaces '%s'", orig_name);
2248 snprintf(temp, sizeof(temp), "%s%d", norm_name, index);
2249 if (!is_device_name_used(verb, temp, device))
2252 } while (index < 100); /* Safety limit */
2254 snd_error(UCM, "too many device name conflicts for '%s'", orig_name);
2261 err = parse_device_index(&norm_name, &index);
2263 snd_error(UCM, "cannot parse device name '%s'", orig_name);
2272 snprintf(temp, sizeof(temp), "%s%d", norm_name, index);
2276 device->name = strdup(temp);
2277 if (device->name == NULL) {
2282 /* Update all references to the old device name */
2283 err = verb_update_device_references(verb, orig_name, device->name);
2300 * Strip index from single device names.
2301 * According to use-case.h specification, if there is only one device
2302 * with a given base name (e.g., only "HDMI1" and no "HDMI2"), the index
2303 * should be stripped to produce the final name (e.g., "HDMI").
2305 static int verb_strip_single_device_index(struct use_case_verb *verb)
2307 struct list_head *pos, *pos2;
2308 struct use_case_device *device, *device2;
2309 char *base_name, *test_base;
2311 int count, index, test_index, err;
2313 list_for_each(pos, &verb->device_list) {
2314 device = list_entry(pos, struct use_case_device, list);
2316 base_name = strdup(device->name);
2317 if (base_name == NULL)
2320 err = parse_device_index(&base_name, &index);
2331 /* Count how many devices have the same base name */
2333 list_for_each(pos2, &verb->device_list) {
2334 device2 = list_entry(pos2, struct use_case_device, list);
2335 test_base = strdup(device2->name);
2336 if (test_base == NULL) {
2341 err = parse_device_index(&test_base, &test_index);
2342 if (err >= 0 && strcmp(test_base, base_name) == 0)
2349 orig_name = device->name;
2350 device->name = base_name;
2352 err = verb_update_device_references(verb, orig_name, device->name);
2354 device->name = orig_name;
2369 * Determine priority for a device.
2371 * 1. If 'Priority' value exists, use it as the sort key
2372 * 2. If 'PlaybackPriority' value exists, use it as the sort key
2373 * 3. If 'CapturePriority' value exists, use it as the sort key
2374 * 4. Fallback: LONG_MIN (no priority)
2376 static long verb_device_get_priority(struct use_case_device *device)
2378 struct list_head *pos;
2379 struct ucm_value *val;
2380 const char *priority_str = NULL;
2381 long priority = LONG_MIN;
2384 list_for_each(pos, &device->value_list) {
2385 val = list_entry(pos, struct ucm_value, list);
2386 if (strcmp(val->name, "Priority") == 0) {
2387 priority_str = val->data;
2392 if (!priority_str) {
2393 list_for_each(pos, &device->value_list) {
2394 val = list_entry(pos, struct ucm_value, list);
2395 if (strcmp(val->name, "PlaybackPriority") == 0) {
2396 priority_str = val->data;
2402 if (!priority_str) {
2403 list_for_each(pos, &device->value_list) {
2404 val = list_entry(pos, struct ucm_value, list);
2405 if (strcmp(val->name, "CapturePriority") == 0) {
2406 priority_str = val->data;
2413 err = safe_strtol(priority_str, &priority);
2415 priority = LONG_MIN;
2422 * Sort devices based on priority values.
2424 * 1. If 'Priority' value exists, use it as the sort key
2425 * 2. If 'PlaybackPriority' value exists, use it as the sort key
2426 * 3. If 'CapturePriority' value exists, use it as the sort key
2427 * 4. Fallback: use device->name (original) as the sort key
2428 * Higher priority values are placed first in the list.
2430 static int verb_sort_devices(struct use_case_verb *verb)
2432 struct list_head sorted_list;
2433 struct list_head *pos, *npos;
2434 struct use_case_device *device, *insert_dev;
2436 INIT_LIST_HEAD(&sorted_list);
2438 /* First pass: determine and cache priority for all devices */
2439 list_for_each(pos, &verb->device_list) {
2440 device = list_entry(pos, struct use_case_device, list);
2441 device->sort_priority = verb_device_get_priority(device);
2444 /* Move devices from verb->device_list to sorted_list in sorted order */
2445 list_for_each_safe(pos, npos, &verb->device_list) {
2446 device = list_entry(pos, struct use_case_device, list);
2448 /* Remove device from original list */
2449 list_del(&device->list);
2451 /* Find the insertion point in sorted_list */
2452 /* Devices are sorted in descending order of priority (higher priority first) */
2453 /* If priorities are equal or not defined, use device name as key */
2454 if (list_empty(&sorted_list)) {
2455 list_add_tail(&device->list, &sorted_list);
2457 struct list_head *pos2, *insert_pos = &sorted_list;
2458 list_for_each(pos2, &sorted_list) {
2459 insert_dev = list_entry(pos2, struct use_case_device, list);
2461 if (device->sort_priority > insert_dev->sort_priority) {
2464 } else if (device->sort_priority == insert_dev->sort_priority) {
2465 if (strcmp(device->name, insert_dev->name) < 0) {
2472 list_add_tail(&device->list, insert_pos);
2476 /* Move sorted list back to verb->device_list */
2477 list_splice_init(&sorted_list, &verb->device_list);
2482 static int verb_device_management(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb)
2484 struct list_head *pos;
2485 struct ucm_dev_name *dev;
2488 /* rename devices */
2489 list_for_each(pos, &verb->rename_list) {
2490 dev = list_entry(pos, struct ucm_dev_name, list);
2491 err = uc_mgr_rename_device(verb, dev->name1, dev->name2);
2493 snd_error(UCM, "cannot rename device '%s' to '%s'", dev->name1, dev->name2);
2498 /* remove devices */
2499 list_for_each(pos, &verb->remove_list) {
2500 dev = list_entry(pos, struct ucm_dev_name, list);
2501 err = uc_mgr_remove_device(verb, dev->name2);
2503 snd_error(UCM, "cannot remove device '%s'", dev->name2);
2508 /* those lists are no longer used */
2509 uc_mgr_free_dev_name_list(&verb->rename_list);
2510 uc_mgr_free_dev_name_list(&verb->remove_list);
2512 /* strip index from single device names */
2513 if (uc_mgr->conf_format >= 8) {
2514 /* sort devices by priority */
2515 err = verb_sort_devices(verb);
2520 /* normalize device names to remove spaces per use-case.h specification */
2521 err = verb_normalize_device_names(uc_mgr, verb);
2525 /* strip index from single device names */
2526 if (uc_mgr->conf_format >= 8) {
2527 err = verb_strip_single_device_index(verb);
2533 /* handle conflicting/supported lists */
2534 return verb_dev_list_check(verb);
2538 * Parse Verb Section
2540 * # Example Use case verb section for Voice call blah
2541 * # By Joe Blogs <joe@blogs.com>
2544 * # enable and disable sequences are compulsory
2546 * cset "name='Master Playback Switch',index=2 0,0"
2547 * cset "name='Master Playback Volume',index=2 25,25"
2549 * cset "name='Master Playback Switch',index=2 1,1"
2550 * cset "name='Master Playback Volume',index=2 50,50"
2554 * cset "name='Master Playback Switch',index=2 0,0"
2555 * cset "name='Master Playback Volume',index=2 25,25"
2557 * cset "name='Master Playback Switch',index=2 1,1"
2558 * cset "name='Master Playback Volume',index=2 50,50"
2561 * # Optional transition verb
2562 * TransitionSequence."ToCaseName" [
2566 * # Optional TQ and ALSA PCMs
2570 * PlaybackPCM "hw:0"
2574 static int parse_verb(snd_use_case_mgr_t *uc_mgr,
2575 struct use_case_verb *verb,
2578 snd_config_iterator_t i, next;
2582 /* in-place evaluation */
2583 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
2587 /* parse verb section */
2588 snd_config_for_each(i, next, cfg) {
2590 n = snd_config_iterator_entry(i);
2591 if (snd_config_get_id(n, &id) < 0)
2594 if (strcmp(id, "EnableSequence") == 0) {
2595 err = parse_sequence(uc_mgr, &verb->enable_list, n);
2597 snd_error(UCM, "failed to parse verb enable sequence");
2603 if (strcmp(id, "DisableSequence") == 0) {
2604 err = parse_sequence(uc_mgr, &verb->disable_list, n);
2606 snd_error(UCM, "failed to parse verb disable sequence");
2612 if (strcmp(id, "TransitionSequence") == 0) {
2613 snd_debug(UCM, "Parse TransitionSequence");
2614 err = parse_transition(uc_mgr, &verb->transition_list, n);
2616 snd_error(UCM, "failed to parse transition sequence");
2622 if (strcmp(id, "Value") == 0) {
2623 err = parse_value(uc_mgr, &verb->value_list, n);
2634 * Parse a Use case verb configuration.
2636 * This configuration contains the following :-
2637 * o Verb enable and disable sequences.
2638 * o Supported Device enable and disable sequences for verb.
2639 * o Supported Modifier enable and disable sequences for verb
2640 * o Optional QoS for the verb and modifiers.
2641 * o Optional PCM device ID for verb and modifiers
2642 * o Alias kcontrols IDs for master and volumes and mutes.
2644 static int parse_verb_config(snd_use_case_mgr_t *uc_mgr,
2645 const char *use_case_name,
2646 const char *comment,
2650 snd_config_iterator_t i, next;
2652 struct use_case_verb *verb;
2656 verb = calloc(1, sizeof(struct use_case_verb));
2659 INIT_LIST_HEAD(&verb->enable_list);
2660 INIT_LIST_HEAD(&verb->disable_list);
2661 INIT_LIST_HEAD(&verb->transition_list);
2662 INIT_LIST_HEAD(&verb->device_list);
2663 INIT_LIST_HEAD(&verb->cmpt_device_list);
2664 INIT_LIST_HEAD(&verb->modifier_list);
2665 INIT_LIST_HEAD(&verb->value_list);
2666 INIT_LIST_HEAD(&verb->rename_list);
2667 INIT_LIST_HEAD(&verb->remove_list);
2668 list_add_tail(&verb->list, &uc_mgr->verb_list);
2669 if (use_case_name == NULL)
2671 verb->name = strdup(use_case_name);
2672 if (verb->name == NULL)
2675 if (comment != NULL) {
2676 verb->comment = strdup(comment);
2677 if (verb->comment == NULL)
2681 /* in-place evaluation */
2682 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
2686 /* parse master config sections */
2687 snd_config_for_each(i, next, cfg) {
2689 n = snd_config_iterator_entry(i);
2690 if (snd_config_get_id(n, &id) < 0)
2693 /* find verb section and parse it */
2694 if (strcmp(id, "SectionVerb") == 0) {
2695 err = parse_verb(uc_mgr, verb, n);
2697 snd_error(UCM, "%s failed to parse verb", what);
2703 /* find device sections and parse them */
2704 if (strcmp(id, "SectionDevice") == 0) {
2705 err = parse_compound(uc_mgr, n,
2706 parse_device_name, verb, NULL);
2708 snd_error(UCM, "%s failed to parse device", what);
2714 /* find modifier sections and parse them */
2715 if (strcmp(id, "SectionModifier") == 0) {
2716 err = parse_compound(uc_mgr, n,
2717 parse_modifier_name, verb, NULL);
2719 snd_error(UCM, "%s failed to parse modifier", what);
2725 /* device renames */
2726 if (strcmp(id, "RenameDevice") == 0) {
2727 err = parse_dev_name_list(uc_mgr, n, &verb->rename_list);
2729 snd_error(UCM, "%s failed to parse device rename", what);
2736 if (strcmp(id, "RemoveDevice") == 0) {
2737 err = parse_dev_name_list(uc_mgr, n, &verb->remove_list);
2739 snd_error(UCM, "%s failed to parse device remove", what);
2745 /* alsa-lib configuration */
2746 if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) {
2747 err = parse_libconfig(uc_mgr, n);
2749 snd_error(UCM, "%s failed to parse LibConfig", what);
2756 /* use case verb must have at least 1 device */
2757 if (list_empty(&verb->device_list)) {
2758 snd_error(UCM, "no use case device defined");
2762 /* do device rename and delete */
2763 err = verb_device_management(uc_mgr, verb);
2765 snd_error(UCM, "device management error in verb '%s'", verb->name);
2773 * Parse variant information
2775 static int parse_variant(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
2776 char **_vfile, char **_vcomment)
2778 snd_config_iterator_t i, next;
2780 char *file = NULL, *comment = NULL;
2783 /* parse master config sections */
2784 snd_config_for_each(i, next, cfg) {
2786 n = snd_config_iterator_entry(i);
2787 if (snd_config_get_id(n, &id) < 0)
2790 /* get use case verb file name */
2791 if (strcmp(id, "File") == 0) {
2793 err = parse_string_substitute3(uc_mgr, n, &file);
2795 snd_error(UCM, "failed to get File");
2802 /* get optional use case comment */
2803 if (strncmp(id, "Comment", 7) == 0) {
2805 err = parse_string_substitute3(uc_mgr, n, &comment);
2807 snd_error(UCM, "failed to get Comment");
2814 snd_error(UCM, "unknown field '%s' in Variant section", id);
2822 *_vcomment = comment;
2832 * Parse master section for "Use Case" and "File" tags.
2834 static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
2835 void *data1 ATTRIBUTE_UNUSED,
2836 void *data2 ATTRIBUTE_UNUSED)
2838 snd_config_iterator_t i, next;
2839 snd_config_t *n, *variant = NULL, *config = NULL;
2840 char *use_case_name, *file = NULL, *comment = NULL;
2841 bool variant_ok = false;
2844 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
2845 snd_error(UCM, "compound type expected for use case section");
2849 err = parse_get_safe_name(uc_mgr, cfg, NULL, &use_case_name);
2851 snd_error(UCM, "unable to get name for use case section");
2855 /* in-place evaluation */
2856 uc_mgr->parse_master_section = 1;
2857 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
2858 uc_mgr->parse_master_section = 0;
2862 /* parse master config sections */
2863 snd_config_for_each(i, next, cfg) {
2865 n = snd_config_iterator_entry(i);
2866 if (snd_config_get_id(n, &id) < 0)
2869 /* get use case verb file name */
2870 if (strcmp(id, "File") == 0) {
2871 err = parse_string_substitute3(uc_mgr, n, &file);
2873 snd_error(UCM, "failed to get File");
2879 /* get use case verb configuration block (syntax version 8+) */
2880 if (strcmp(id, "Config") == 0) {
2881 if (uc_mgr->conf_format < 8) {
2882 snd_error(UCM, "Config is supported in v8+ syntax");
2886 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
2887 snd_error(UCM, "compound type expected for Config");
2895 /* get optional use case comment */
2896 if (strncmp(id, "Comment", 7) == 0) {
2897 err = parse_string_substitute3(uc_mgr, n, &comment);
2899 snd_error(UCM, "failed to get Comment");
2905 if (uc_mgr->conf_format >= 6 && strcmp(id, "Variant") == 0) {
2906 snd_config_iterator_t i2, next2;
2908 snd_config_for_each(i2, next2, n) {
2911 n2 = snd_config_iterator_entry(i2);
2912 if (snd_config_get_id(n2, &id2) < 0)
2914 err = uc_mgr_evaluate_inplace(uc_mgr, n2);
2917 if (strcmp(use_case_name, id2) == 0)
2923 snd_error(UCM, "unknown field '%s' in SectionUseCase", id);
2926 if (variant && !variant_ok) {
2927 snd_error(UCM, "undefined variant '%s'", use_case_name);
2932 /* check mutual exclusivity of File and Config */
2933 if (file && config) {
2934 snd_error(UCM, "both File and Config specified in SectionUseCase");
2940 snd_debug(UCM, "use_case_name %s file '%s'", use_case_name, file);
2942 /* do we have both use case name and (file or config) ? */
2943 if (!file && !config) {
2944 snd_error(UCM, "use case missing file or config");
2949 /* parse verb from file or config */
2952 /* load config from file */
2953 err = uc_mgr_config_load_file(uc_mgr, file, &cfg, false);
2956 /* parse the config */
2957 err = parse_verb_config(uc_mgr, use_case_name, comment, cfg, file);
2958 snd_config_delete(cfg);
2960 /* inline config - parse directly */
2961 err = parse_verb_config(uc_mgr, use_case_name, comment, config,
2962 comment ? comment : use_case_name);
2965 /* parse variants */
2966 struct list_head orig_variable_list;
2967 snd_config_t *orig_macros = NULL;
2968 int first_iteration = 1;
2970 /* save original variable list */
2971 err = uc_mgr_duplicate_variables(&orig_variable_list, &uc_mgr->variable_list);
2975 /* save original macros */
2976 if (uc_mgr->macros) {
2977 err = snd_config_copy(&orig_macros, uc_mgr->macros);
2979 goto __variant_error;
2982 snd_config_for_each(i, next, variant) {
2983 char *vfile, *vcomment;
2986 /* restore variables and macros for second and later iterations */
2987 if (!first_iteration) {
2988 uc_mgr_free_value(&uc_mgr->variable_list);
2990 err = uc_mgr_duplicate_variables(&uc_mgr->variable_list, &orig_variable_list);
2992 goto __variant_error;
2994 if (uc_mgr->macros) {
2995 snd_config_delete(uc_mgr->macros);
2996 uc_mgr->macros = NULL;
2999 err = snd_config_copy(&uc_mgr->macros, orig_macros);
3001 goto __variant_error;
3004 first_iteration = 0;
3006 n = snd_config_iterator_entry(i);
3007 if (snd_config_get_id(n, &id) < 0)
3009 if (!parse_is_name_safe(id)) {
3011 goto __variant_error;
3013 err = parse_variant(uc_mgr, n, &vfile, &vcomment);
3016 uc_mgr->parse_variant = id;
3017 if (vfile || file) {
3019 const char *fname = vfile ? vfile : file;
3020 /* load config from file */
3021 err = uc_mgr_config_load_file(uc_mgr, fname, &cfg, false);
3023 err = parse_verb_config(uc_mgr, id,
3024 vcomment ? vcomment : comment,
3026 snd_config_delete(cfg);
3029 /* inline config from variant */
3030 err = parse_verb_config(uc_mgr, id,
3031 vcomment ? vcomment : comment,
3033 vcomment ? vcomment : (comment ? comment : id));
3035 uc_mgr->parse_variant = NULL;
3043 uc_mgr_free_value(&orig_variable_list);
3045 snd_config_delete(orig_macros);
3049 free(use_case_name);
3056 * parse controls which should be run only at initial boot (forcefully)
3058 static int parse_controls_fixedboot(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
3062 if (!list_empty(&uc_mgr->fixedboot_list)) {
3063 snd_error(UCM, "FixedBoot list is not empty");
3066 err = parse_sequence(uc_mgr, &uc_mgr->fixedboot_list, cfg);
3068 snd_error(UCM, "Unable to parse FixedBootSequence");
3076 * parse controls which should be run only at initial boot
3078 static int parse_controls_boot(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
3082 if (!list_empty(&uc_mgr->boot_list)) {
3083 snd_error(UCM, "Boot list is not empty");
3086 err = parse_sequence(uc_mgr, &uc_mgr->boot_list, cfg);
3088 snd_error(UCM, "Unable to parse BootSequence");
3098 static int parse_controls(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
3102 if (!list_empty(&uc_mgr->default_list)) {
3103 snd_error(UCM, "Default list is not empty");
3106 err = parse_sequence(uc_mgr, &uc_mgr->default_list, cfg);
3108 snd_error(UCM, "Unable to parse SectionDefaults");
3116 * Each sound card has a master sound card file that lists all the supported
3117 * use case verbs for that sound card. i.e.
3119 * #Example master file for blah sound card
3120 * #By Joe Blogs <joe@bloggs.org>
3122 * Comment "Nice Abstracted Soundcard"
3124 * # The file is divided into Use case sections. One section per use case verb.
3126 * SectionUseCase."Voice Call" {
3127 * File "voice_call_blah"
3128 * Comment "Make a voice phone call."
3131 * SectionUseCase."HiFi" {
3133 * Comment "Play and record HiFi quality Music."
3136 * # Define Value defaults
3139 * PlaybackCTL "hw:CARD=0"
3140 * CaptureCTL "hw:CARD=0"
3143 * # The initial boot (run once) configuration.
3146 * cset "name='Master Playback Switch',index=2 1,1"
3147 * cset "name='Master Playback Volume',index=2 25,25"
3150 * # This file also stores the default sound card state.
3153 * cset "name='Master Mono Playback',index=1 0"
3154 * cset "name='Master Mono Playback Volume',index=1 0"
3155 * cset "name='PCM Switch',index=2 1,1"
3156 * exec "some binary here"
3161 * # End of example file.
3163 static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
3165 snd_config_iterator_t i, next;
3170 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
3171 snd_error(UCM, "compound type expected for master file");
3175 if (uc_mgr->conf_format >= 2) {
3176 err = parse_syntax_field(uc_mgr, cfg, uc_mgr->conf_file_name);
3181 /* in-place evaluation */
3182 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
3186 /* parse ValueGlobals first */
3187 err = snd_config_search(cfg, "ValueGlobals", &n);
3189 err = parse_value(uc_mgr, &uc_mgr->global_value_list, n);
3191 snd_error(UCM, "failed to parse ValueGlobals");
3196 err = uc_mgr_check_value(&uc_mgr->global_value_list, "BootCardGroup");
3198 uc_mgr->card_group = true;
3199 /* if we are in boot, skip the main parsing loop */
3200 if (uc_mgr->in_boot)
3204 /* parse master config sections */
3205 snd_config_for_each(i, next, cfg) {
3207 n = snd_config_iterator_entry(i);
3208 if (snd_config_get_id(n, &id) < 0)
3211 if (strcmp(id, "Comment") == 0) {
3212 err = parse_string_substitute3(uc_mgr, n, &uc_mgr->comment);
3214 snd_error(UCM, "failed to get master comment");
3220 /* find use case section and parse it */
3221 if (strcmp(id, "SectionUseCase") == 0) {
3222 err = parse_compound(uc_mgr, n,
3223 parse_master_section,
3230 /* find default control values section (force boot sequence only) */
3231 if (strcmp(id, "FixedBootSequence") == 0) {
3232 err = parse_controls_fixedboot(uc_mgr, n);
3238 /* find default control values section (first boot only) */
3239 if (strcmp(id, "BootSequence") == 0) {
3240 err = parse_controls_boot(uc_mgr, n);
3246 /* find default control values section and parse it */
3247 if (strcmp(id, "SectionDefaults") == 0) {
3248 err = parse_controls(uc_mgr, n);
3254 /* ValueDefaults is now parsed at the top of this function */
3255 if (strcmp(id, "ValueDefaults") == 0) {
3256 err = parse_value(uc_mgr, &uc_mgr->value_list, n);
3258 snd_error(UCM, "failed to parse ValueDefaults");
3264 /* ValueGlobals is parsed at the top of this function */
3265 if (strcmp(id, "ValueGlobals") == 0)
3268 /* alsa-lib configuration */
3269 if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) {
3270 err = parse_libconfig(uc_mgr, n);
3272 snd_error(UCM, "failed to parse LibraryConfig");
3279 if (strcmp(id, "Error") == 0)
3280 return error_node(uc_mgr, n);
3282 /* skip further Syntax value updates (Include) */
3283 if (strcmp(id, "Syntax") == 0)
3286 snd_error(UCM, "unknown master file field %s", id);
3291 /* get the card info */
3292 static int get_card_info(snd_use_case_mgr_t *mgr,
3293 const char *ctl_name,
3294 snd_ctl_card_info_t **info)
3296 struct ctl_list *ctl_list;
3299 err = uc_mgr_open_ctl(mgr, &ctl_list, ctl_name, 0);
3304 *info = ctl_list->ctl_info;
3308 /* find the card in the local machine */
3309 static int get_by_card_name(snd_use_case_mgr_t *mgr, const char *card_name)
3312 snd_ctl_card_info_t *info;
3313 const char *_driver, *_name, *_long_name;
3315 snd_ctl_card_info_alloca(&info);
3318 if (snd_card_next(&card) < 0 || card < 0) {
3319 snd_error(UCM, "no soundcards found...");
3326 /* clear the list, keep the only one CTL device */
3327 uc_mgr_free_ctl_list(mgr);
3329 sprintf(name, "hw:%d", card);
3330 err = get_card_info(mgr, name, &info);
3333 _driver = snd_ctl_card_info_get_driver(info);
3334 _name = snd_ctl_card_info_get_name(info);
3335 _long_name = snd_ctl_card_info_get_longname(info);
3336 if (!strcmp(card_name, _driver) ||
3337 !strcmp(card_name, _name) ||
3338 !strcmp(card_name, _long_name))
3342 if (snd_card_next(&card) < 0) {
3343 snd_error(UCM, "snd_card_next");
3348 uc_mgr_free_ctl_list(mgr);
3353 /* set the driver name and long name by the card ctl name */
3354 static inline int get_by_card(snd_use_case_mgr_t *mgr, const char *ctl_name)
3356 return get_card_info(mgr, ctl_name, NULL);
3359 static int parse_toplevel_path(snd_use_case_mgr_t *uc_mgr,
3363 snd_config_iterator_t i, next, i2, next2;
3364 snd_config_t *n, *n2;
3366 char *dir = NULL, *file = NULL, fn[PATH_MAX];
3371 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
3372 snd_error(UCM, "compound type expected for UseCasePath node");
3376 /* parse use case path config sections */
3377 snd_config_for_each(i, next, cfg) {
3378 n = snd_config_iterator_entry(i);
3380 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
3381 snd_error(UCM, "compound type expected for UseCasePath.something node");
3385 if (snd_config_get_id(n, &id) < 0)
3390 /* parse use case path config sections */
3391 snd_config_for_each(i2, next2, n) {
3393 n2 = snd_config_iterator_entry(i2);
3394 if (snd_config_get_id(n2, &id) < 0)
3397 if (strcmp(id, "Version") == 0) {
3398 err = parse_integer_substitute(uc_mgr, n2, &version);
3400 snd_error(UCM, "unable to parse UcmDirectory");
3403 if (version < 1 || version > 2) {
3404 snd_error(UCM, "Version must be 1 or 2");
3411 if (strcmp(id, "Directory") == 0) {
3412 err = parse_string_substitute(uc_mgr, n2, &dir);
3414 snd_error(UCM, "unable to parse Directory");
3420 if (strcmp(id, "File") == 0) {
3421 err = parse_string_substitute(uc_mgr, n2, &file);
3423 snd_error(UCM, "unable to parse File");
3429 snd_error(UCM, "unknown UseCasePath field %s", id);
3433 snd_error(UCM, "Directory is not defined in %s!", filename);
3437 snd_error(UCM, "File is not defined in %s!", filename);
3441 ucm_filename(fn, sizeof(fn), version, dir, file);
3442 snd_trace(UCM, "probing configuration file '%s'", fn);
3443 if (access(fn, R_OK) == 0 && lstat64(fn, &st) == 0) {
3444 if (S_ISLNK(st.st_mode)) {
3446 char *link, *dir2, *p;
3448 link = malloc(PATH_MAX);
3451 r = readlink(fn, link, PATH_MAX - 1);
3457 p = strrchr(link, '/');
3460 dir2 = malloc(PATH_MAX);
3465 strncpy(dir2, dir, PATH_MAX - 1);
3466 strncat(dir2, "/", PATH_MAX - 1);
3467 strncat(dir2, link, PATH_MAX - 1);
3468 fn[PATH_MAX - 1] = '\0';
3474 snd_trace(UCM, "using directory '%s' and file '%s'", dir, file);
3475 if (replace_string(&uc_mgr->conf_dir_name, dir) == NULL)
3477 if (replace_string(&uc_mgr->conf_file_name, file) == NULL)
3479 strncpy(filename, fn, PATH_MAX);
3480 filename[PATH_MAX - 1] = '\0';
3481 uc_mgr->conf_format = version;
3509 static int parse_toplevel_config(snd_use_case_mgr_t *uc_mgr,
3513 snd_config_iterator_t i, next;
3518 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
3519 snd_error(UCM, "compound type expected for toplevel file");
3523 err = parse_syntax_field(uc_mgr, cfg, filename);
3527 /* in-place evaluation */
3528 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
3532 /* parse toplevel config sections */
3533 snd_config_for_each(i, next, cfg) {
3535 n = snd_config_iterator_entry(i);
3536 if (snd_config_get_id(n, &id) < 0)
3539 if (strcmp(id, "UseCasePath") == 0) {
3540 err = parse_toplevel_path(uc_mgr, filename, n);
3546 /* alsa-lib configuration */
3547 if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) {
3548 err = parse_libconfig(uc_mgr, n);
3550 snd_error(UCM, "failed to parse LibConfig");
3556 /* skip further Syntax value updates (Include) */
3557 if (strcmp(id, "Syntax") == 0)
3560 snd_error(UCM, "unknown toplevel field %s", id);
3566 static int load_toplevel_config(snd_use_case_mgr_t *uc_mgr,
3569 char filename[PATH_MAX];
3573 ucm_filename(filename, sizeof(filename), 2, NULL, "ucm.conf");
3575 if (access(filename, R_OK) != 0) {
3576 snd_error(UCM, "Unable to find the top-level configuration file '%s'.", filename);
3580 err = uc_mgr_config_load(2, filename, &tcfg, false);
3584 /* filename is shared for function input and output! */
3585 err = parse_toplevel_config(uc_mgr, filename, tcfg);
3586 snd_config_delete(tcfg);
3590 err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg, false);
3592 snd_error(UCM, "could not parse configuration for card %s", uc_mgr->card_name);
3602 /* load master use case file for sound card based on rules in ucm2/ucm.conf
3604 int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
3610 err = snd_config_top(&uc_mgr->local_config);
3614 err = snd_config_top(&uc_mgr->macros);
3618 name = uc_mgr->card_name;
3619 if (strncmp(name, "hw:", 3) == 0) {
3620 err = get_by_card(uc_mgr, name);
3622 snd_error(UCM, "card '%s' is not valid", name);
3625 } else if (strncmp(name, "strict:", 7)) {
3626 /* do not handle the error here */
3627 /* we can refer the virtual UCM config */
3628 get_by_card_name(uc_mgr, name);
3631 err = load_toplevel_config(uc_mgr, &cfg);
3635 err = parse_master_file(uc_mgr, cfg);
3636 if (uc_mgr->macros) {
3637 snd_config_delete(uc_mgr->macros);
3638 uc_mgr->macros = NULL;
3640 snd_config_delete(cfg);
3642 uc_mgr_free_ctl_list(uc_mgr);
3643 uc_mgr_free_verb(uc_mgr);
3649 uc_mgr_free_ctl_list(uc_mgr);
3650 replace_string(&uc_mgr->conf_dir_name, NULL);
3654 static int filename_filter(const struct dirent64 *dirent)
3658 if (dirent->d_type == DT_DIR) {
3659 if (dirent->d_name[0] == '.') {
3660 if (dirent->d_name[1] == '\0')
3662 if (dirent->d_name[1] == '.' &&
3663 dirent->d_name[2] == '\0')
3671 /* scan all cards and comments
3673 * Cards are defined by machines. Each card/machine installs its UCM
3674 * configuration files in a subdirectory with the same name as the sound
3675 * card under /usr/share/alsa/ucm2. This function will scan all the card
3676 * directories and skip the component directories defined in the array
3679 int uc_mgr_scan_master_configs(const char **_list[])
3681 char filename[PATH_MAX], dfl[PATH_MAX], fn[FILENAME_MAX];
3682 char *env = getenv(ALSA_CONFIG_UCM2_VAR);
3683 snd_use_case_mgr_t *uc_mgr;
3684 const char **list, *d_name;
3686 snd_config_t *cfg, *c;
3687 int i, j, cnt, err, cards;
3690 struct dirent64 **namelist;
3695 err = snd_card_next(&i);
3702 cards += 4; /* plug-and-play */
3705 snprintf(filename, sizeof(filename), "%s/conf.virt.d", env);
3707 snprintf(filename, sizeof(filename), "%s/ucm2/conf.virt.d",
3708 snd_config_topdir());
3710 #if defined(_GNU_SOURCE) && \
3711 !defined(__NetBSD__) && \
3712 !defined(__FreeBSD__) && \
3713 !defined(__OpenBSD__) && \
3714 !defined(__DragonFly__) && \
3715 !defined(__sun) && \
3716 !defined(__ANDROID__) && \
3718 #define SORTFUNC versionsort64
3720 #define SORTFUNC alphasort64
3722 err = scandir64(filename, &namelist, filename_filter, SORTFUNC);
3725 snd_error(UCM, "could not scan directory %s: %s", filename, strerror(-err));
3731 if (strlen(filename) + 8 < sizeof(filename)) {
3732 strcat(filename, "/default");
3733 ss = readlink(filename, dfl, sizeof(dfl)-1);
3736 dfl[sizeof(dfl)-1] = '\0';
3737 if (dfl[0] && dfl[strlen(dfl)-1] == '/')
3738 dfl[strlen(dfl)-1] = '\0';
3745 list = calloc(1, (cards + cnt) * 2 * sizeof(char *));
3752 while (j / 2 < cards) {
3753 err = snd_card_next(&i);
3758 snprintf(fn, sizeof(fn), "-hw:%d", i);
3759 err = snd_use_case_mgr_open(&uc_mgr, fn);
3760 if (err == -ENOENT || err == -ENXIO)
3763 snd_error(UCM, "Unable to open '%s': %s", fn, snd_strerror(err));
3766 err = snd_use_case_get(uc_mgr, "comment", (const char **)&s);
3768 err = snd_card_get_longname(i, &s);
3772 snd_use_case_mgr_close(uc_mgr);
3773 list[j] = strdup(fn + 1);
3774 if (list[j] == NULL) {
3783 for (i = 0; i < cnt; i++) {
3785 d_name = namelist[i]->d_name;
3787 snprintf(fn, sizeof(fn), "%s.conf", d_name);
3788 ucm_filename(filename, sizeof(filename), 2, d_name, fn);
3790 if (eaccess(filename, R_OK))
3792 if (access(filename, R_OK))
3796 err = uc_mgr_config_load(2, filename, &cfg, false);
3799 err = snd_config_search(cfg, "Syntax", &c);
3801 snd_error(UCM, "Syntax field not found in %s", d_name);
3802 snd_config_delete(cfg);
3805 err = snd_config_get_integer(c, &l);
3807 snd_error(UCM, "Syntax field is invalid in %s", d_name);
3808 snd_config_delete(cfg);
3811 if (l < 2 || l > SYNTAX_VERSION_MAX) {
3812 snd_error(UCM, "Incompatible syntax %d in %s", l, d_name);
3813 snd_config_delete(cfg);
3816 err = snd_config_search(cfg, "Comment", &c);
3818 err = parse_string(c, (char **)&list[j+1]);
3820 snd_config_delete(cfg);
3824 snd_config_delete(cfg);
3825 list[j] = strdup(d_name);
3826 if (list[j] == NULL) {
3830 if (strcmp(dfl, list[j]) == 0) {
3831 /* default to top */
3832 const char *save1 = list[j];
3833 const char *save2 = list[j + 1];
3834 memmove(list + 2, list, j * sizeof(char *));
3843 for (i = 0; i < cnt; i++)
3847 for (i = 0; i < j; i++) {
3848 free((void *)list[i * 2]);
3849 free((void *)list[i * 2 + 1]);