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 int 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);
56 if (snd_relative_path_check(file) < 0 ||
57 (dir && snd_relative_path_check(dir) < 0)) {
58 snd_error(UCM, "config file path '%s%s%s' escapes the configuration directory",
59 dir ?: "", dir ? "/" : "", file);
63 snprintf(fn, fn_len, "%s/%s/%s%s%s",
64 snd_config_topdir(), version > 1 ? "ucm2" : "ucm",
65 dir ?: "", dir ? "/" : "", file);
67 snprintf(fn, fn_len, "%s/%s%s%s",
68 env, dir ?: "", dir ? "/" : "", file);
75 int uc_mgr_config_load_file(snd_use_case_mgr_t *uc_mgr,
76 const char *file, snd_config_t **cfg, bool optional)
78 char filename[PATH_MAX];
81 err = ucm_filename(filename, sizeof(filename), uc_mgr->conf_format,
82 file[0] == '/' ? NULL : uc_mgr->conf_dir_name,
86 err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg, optional);
88 if (!optional || (err != -ENOENT && err != -EACCES))
89 snd_error(UCM, "failed to open file %s: %d", filename, err);
96 * Replace mallocated string
98 static char *replace_string(char **dst, const char *value)
101 *dst = value ? strdup(value) : NULL;
108 static int parse_string(snd_config_t *n, char **res)
112 err = snd_config_get_string(n, (const char **)res);
122 * Parse string and substitute
124 static int parse_string_substitute(snd_use_case_mgr_t *uc_mgr,
125 snd_config_t *n, char **res)
131 err = snd_config_get_string(n, &str);
134 err = uc_mgr_get_substituted_value(uc_mgr, &s, str);
141 * Parse string and substitute
143 static int parse_string_substitute3(snd_use_case_mgr_t *uc_mgr,
144 snd_config_t *n, char **res)
146 if (uc_mgr->conf_format < 3)
147 return parse_string(n, res);
148 return parse_string_substitute(uc_mgr, n, res);
152 * Parse integer with substitution
154 static int parse_integer_substitute(snd_use_case_mgr_t *uc_mgr,
155 snd_config_t *n, long *res)
160 err = snd_config_get_ascii(n, &s1);
163 err = uc_mgr_get_substituted_value(uc_mgr, &s2, s1);
165 err = safe_strtol(s2, res);
172 * Parse integer with substitution
174 static int parse_integer_substitute3(snd_use_case_mgr_t *uc_mgr,
175 snd_config_t *n, long *res)
180 err = snd_config_get_ascii(n, &s1);
183 if (uc_mgr->conf_format < 3)
186 err = uc_mgr_get_substituted_value(uc_mgr, &s2, s1);
188 err = safe_strtol(s2, res);
198 static int parse_is_name_safe(const char *name)
200 if (strchr(name, '.')) {
201 snd_error(UCM, "char '.' not allowed in '%s'", name);
207 static int get_string3(snd_use_case_mgr_t *uc_mgr, const char *s1, char **s)
209 if (uc_mgr->conf_format < 3) {
215 return uc_mgr_get_substituted_value(uc_mgr, s, s1);
218 static int parse_get_safe_name(snd_use_case_mgr_t *uc_mgr, snd_config_t *n,
219 const char *alt, char **name)
227 err = snd_config_get_id(n, &id);
231 err = get_string3(uc_mgr, id, name);
234 if (!parse_is_name_safe(*name)) {
242 * Parse device index from device name
243 * According to use-case.h specification, device names can have numeric suffixes
244 * like HDMI1, HDMI2, or "Line 1", "Line 2".
245 * This function extracts the index and modifies the name to contain only the base.
247 static int parse_device_index(char **name, int *index)
254 if (!name || !*name || !index)
261 /* Start from the end and find where digits begin */
264 /* Skip trailing digits */
265 while (p > *name && isdigit(*p))
268 /* If no digits found at the end, index is 0 (no index) */
269 if (p == *name + len - 1) {
274 /* Move to first digit */
277 /* Check if there's an optional space before the number */
278 if (p > *name && *(p - 1) == ' ')
281 /* Parse the index */
282 num_start = *p == ' ' ? p + 1 : p;
283 err = safe_strtol(num_start, &idx);
285 /* No valid number found */
292 /* Truncate the name to remove the index part */
294 *p = '\0'; /* Remove space and number */
296 *p = '\0'; /* Remove number only */
302 * Handle 'Error' configuration node.
304 static int error_node(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
309 err = parse_string_substitute3(uc_mgr, cfg, &s);
311 snd_error(UCM, "failed to get Error string");
314 if (!uc_mgr->suppress_nodev_errors)
315 snd_error(UCM, "%s", s);
323 static int parse_syntax_field(snd_use_case_mgr_t *uc_mgr,
324 snd_config_t *cfg, const char *filename)
330 err = snd_config_search(cfg, "Syntax", &n);
332 snd_error(UCM, "Syntax field not found in %s", filename);
335 err = snd_config_get_integer(n, &l);
337 snd_error(UCM, "Syntax field is invalid in %s", filename);
340 if (l < 2 || l > SYNTAX_VERSION_MAX) {
341 snd_error(UCM, "Incompatible syntax %ld in %s", l, filename);
344 /* delete this field to optimize strcmp() call in the parsing loop */
345 snd_config_delete(n);
346 uc_mgr->conf_format = l;
351 * Evaluate variable regex definitions (in-place delete)
353 static int evaluate_regex(snd_use_case_mgr_t *uc_mgr,
356 snd_config_iterator_t i, next;
361 err = snd_config_search(cfg, "DefineRegex", &d);
367 if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
368 snd_error(UCM, "compound type expected for DefineRegex");
372 if (uc_mgr->conf_format < 3) {
373 snd_error(UCM, "DefineRegex is supported in v3+ syntax");
377 snd_config_for_each(i, next, d) {
378 n = snd_config_iterator_entry(i);
379 err = snd_config_get_id(n, &id);
383 snd_error(UCM, "value names starting with '@' are reserved for application variables");
386 err = uc_mgr_define_regex(uc_mgr, id, n);
391 snd_config_delete(d);
396 * Evaluate variable definitions (in-place delete)
398 static int evaluate_define(snd_use_case_mgr_t *uc_mgr,
401 snd_config_iterator_t i, next;
407 err = snd_config_search(cfg, "Define", &d);
409 return evaluate_regex(uc_mgr, cfg);
413 if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
414 snd_error(UCM, "compound type expected for Define");
418 if (uc_mgr->conf_format < 3) {
419 snd_error(UCM, "Define is supported in v3+ syntax");
423 snd_config_for_each(i, next, d) {
424 n = snd_config_iterator_entry(i);
425 err = snd_config_get_id(n, &id);
428 err = snd_config_get_ascii(n, &var);
431 err = uc_mgr_get_substituted_value(uc_mgr, &s, var);
437 snd_error(UCM, "value names starting with '@' are reserved for application variables");
441 if (uc_mgr->conf_format >= 9) {
442 err = uc_mgr_get_substituted_value(uc_mgr, &sid, id);
448 err = uc_mgr_set_variable(uc_mgr, sid, s);
456 snd_config_delete(d);
458 return evaluate_regex(uc_mgr, cfg);
462 * Evaluate macro definitions (in-place delete)
464 static int evaluate_define_macro(snd_use_case_mgr_t *uc_mgr,
470 err = snd_config_search(cfg, "DefineMacro", &d);
476 if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
477 snd_error(UCM, "compound type expected for DefineMacro");
481 if (uc_mgr->conf_format < 6) {
482 snd_error(UCM, "DefineMacro is supported in v6+ syntax");
486 err = snd_config_merge(uc_mgr->macros, d, 0);
493 static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr,
497 snd_config_iterator_t i, next;
498 snd_config_t *m, *mc, *a, *n;
499 const char *mid, *id;
500 char name[128], *var, *var2;
504 err = snd_config_get_id(args, &mid);
507 err = snd_config_search(uc_mgr->macros, mid, &m);
509 snd_error(UCM, "Macro '%s' is not defined", mid);
514 if (snd_config_get_type(args) == SND_CONFIG_TYPE_STRING) {
515 err = snd_config_get_string(args, &s);
518 if (uc_mgr->conf_format < 9) {
519 err = snd_config_load_string(&a, s, 0);
521 err = uc_mgr_get_substituted_value(uc_mgr, &var2, s);
523 err = snd_config_load_string(&a, var2, 0);
529 } else if (snd_config_get_type(args) != SND_CONFIG_TYPE_COMPOUND) {
534 snd_config_for_each(i, next, a) {
535 n = snd_config_iterator_entry(i);
536 err = snd_config_get_id(n, &id);
539 snprintf(name, sizeof(name), "__%s", id);
540 if (uc_mgr_get_variable(uc_mgr, name, false)) {
541 snd_error(UCM, "Macro argument '%s' is already defined", name);
544 err = snd_config_get_ascii(n, &var);
547 if (uc_mgr->conf_format < 7) {
548 err = uc_mgr_set_variable(uc_mgr, name, var);
551 err = uc_mgr_get_substituted_value(uc_mgr, &var2, var);
554 err = uc_mgr_set_variable(uc_mgr, name, var2);
562 /* merge + substitute variables */
563 err = snd_config_copy(&mc, m);
566 err = uc_mgr_evaluate_inplace(uc_mgr, mc);
568 snd_config_delete(mc);
571 err = uc_mgr_config_tree_merge(uc_mgr, dst, mc, NULL, NULL);
572 snd_config_delete(mc);
574 /* delete arguments */
575 snd_config_for_each(i, next, a) {
576 n = snd_config_iterator_entry(i);
577 err = snd_config_get_id(n, &id);
580 snprintf(name, sizeof(name), "__%s", id);
581 err = uc_mgr_delete_variable(uc_mgr, name);
588 snd_config_delete(a);
593 * Evaluate macro definitions and instances (in-place delete)
595 static int evaluate_macro(snd_use_case_mgr_t *uc_mgr,
598 snd_config_iterator_t i, i2, next, next2;
599 snd_config_t *d, *n, *n2;
602 ret = evaluate_define_macro(uc_mgr, cfg);
606 err = snd_config_search(cfg, "Macro", &d);
612 if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
613 snd_error(UCM, "compound type expected for DefineMacro");
617 if (uc_mgr->conf_format < 6) {
618 snd_error(UCM, "Macro is supported in v6+ syntax");
622 snd_config_for_each(i, next, d) {
623 n = snd_config_iterator_entry(i);
624 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
626 if (snd_config_get_id(n, &id))
628 snd_error(UCM, "compound type expected for Macro.%s", id);
631 snd_config_for_each(i2, next2, n) {
632 n2 = snd_config_iterator_entry(i2);
633 err = evaluate_macro1(uc_mgr, cfg, n2);
639 snd_config_delete(d);
645 * Evaluate include (in-place)
647 static int evaluate_include(snd_use_case_mgr_t *uc_mgr,
653 err = snd_config_search(cfg, "Include", &n);
659 err = uc_mgr_evaluate_include(uc_mgr, cfg, n);
660 snd_config_delete(n);
665 * Evaluate condition (in-place)
667 static int evaluate_condition(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
672 err = snd_config_search(cfg, "If", &n);
678 err = uc_mgr_evaluate_condition(uc_mgr, cfg, n);
679 snd_config_delete(n);
684 * Evaluate variant (in-place)
686 static int evaluate_variant(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
688 snd_config_iterator_t i, next;
693 err = snd_config_search(cfg, "Variant", &c);
699 if (uc_mgr->conf_format < 6) {
700 snd_error(UCM, "Variant is supported in v6+ syntax");
704 if (uc_mgr->parse_master_section)
707 if (uc_mgr->parse_variant == NULL)
710 snd_config_for_each(i, next, c) {
711 n = snd_config_iterator_entry(i);
713 if (snd_config_get_id(n, &id) < 0)
716 if (strcmp(id, uc_mgr->parse_variant))
719 err = uc_mgr_evaluate_inplace(uc_mgr, n);
723 err = uc_mgr_config_tree_merge(uc_mgr, cfg, n, NULL, NULL);
726 snd_config_delete(c);
731 snd_config_delete(c);
738 int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
741 long iterations = 10000;
742 int err1 = 0, err2 = 0, err3 = 0, err4 = 0, err5 = 0, err6 = 0;
744 while (err1 == 0 || err2 == 0 || err3 == 0 || err4 == 0 || err5 == 0 || err6 == 0) {
745 if (iterations == 0) {
746 snd_error(UCM, "Maximal inplace evaluation iterations number reached (recursive references?)");
750 /* variables at first */
751 err1 = evaluate_define(uc_mgr, cfg);
754 /* include at second */
755 err2 = evaluate_include(uc_mgr, cfg);
758 /* include or macro may define another variables */
759 /* conditions may depend on them */
762 err3 = evaluate_variant(uc_mgr, cfg);
767 uc_mgr->macro_hops++;
768 if (uc_mgr->macro_hops > 100) {
769 snd_error(UCM, "Maximal macro hops reached!");
772 err4 = evaluate_macro(uc_mgr, cfg);
773 uc_mgr->macro_hops--;
778 err5 = uc_mgr_evaluate_repeat(uc_mgr, cfg);
781 err6 = evaluate_condition(uc_mgr, cfg);
789 * Parse one item for alsa-lib config
791 static int parse_libconfig1(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
793 snd_config_iterator_t i, next;
794 snd_config_t *n, *config = NULL;
795 const char *id, *file = NULL;
796 bool substfile = false, substconfig = false;
799 if (snd_config_get_id(cfg, &id) < 0)
802 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
803 snd_error(UCM, "compound type expected for %s", id);
807 snd_config_for_each(i, next, cfg) {
808 n = snd_config_iterator_entry(i);
810 if (snd_config_get_id(n, &id) < 0)
813 if (strcmp(id, "File") == 0 ||
814 strcmp(id, "SubstiFile") == 0) {
815 substfile = id[0] == 'S';
816 err = snd_config_get_string(n, &file);
822 if (strcmp(id, "Config") == 0 ||
823 strcmp(id, "SubstiConfig") == 0) {
824 substconfig = id[0] == 'S';
825 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND)
831 snd_error(UCM, "unknown field %s", id);
838 err = uc_mgr_config_load_file(uc_mgr, file, &cfg, false);
841 err = uc_mgr_substitute_tree(uc_mgr, cfg);
843 snd_config_delete(cfg);
846 err = snd_config_merge(uc_mgr->local_config, cfg, 0);
848 snd_config_delete(cfg);
852 char filename[PATH_MAX];
854 err = ucm_filename(filename, sizeof(filename), uc_mgr->conf_format,
855 file[0] == '/' ? NULL : uc_mgr->conf_dir_name,
859 err = uc_mgr_config_load_into(uc_mgr->conf_format, filename, uc_mgr->local_config, false);
867 err = uc_mgr_substitute_tree(uc_mgr, config);
871 err = snd_config_merge(uc_mgr->local_config, config, 0);
880 * Parse alsa-lib config
882 static int parse_libconfig(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
884 snd_config_iterator_t i, next;
889 if (snd_config_get_id(cfg, &id) < 0)
892 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
893 snd_error(UCM, "compound type expected for %s", id);
897 snd_config_for_each(i, next, cfg) {
898 n = snd_config_iterator_entry(i);
900 err = parse_libconfig1(uc_mgr, n);
911 static int parse_transition(snd_use_case_mgr_t *uc_mgr,
912 struct list_head *tlist,
915 struct transition_sequence *tseq;
917 snd_config_iterator_t i, next;
921 if (snd_config_get_id(cfg, &id) < 0)
924 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
925 snd_error(UCM, "compound type expected for %s", id);
929 snd_config_for_each(i, next, cfg) {
930 n = snd_config_iterator_entry(i);
932 if (snd_config_get_id(n, &id) < 0)
935 tseq = calloc(1, sizeof(*tseq));
938 INIT_LIST_HEAD(&tseq->transition_list);
940 err = get_string3(uc_mgr, id, &tseq->name);
946 err = parse_sequence(uc_mgr, &tseq->transition_list, n);
948 uc_mgr_free_transition_element(tseq);
952 list_add(&tseq->list, tlist);
960 static int parse_compound(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
961 int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
962 void *data1, void *data2)
965 snd_config_iterator_t i, next;
969 if (snd_config_get_id(cfg, &id) < 0)
972 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
973 snd_error(UCM, "compound type expected for %s", id);
977 snd_config_for_each(i, next, cfg) {
978 n = snd_config_iterator_entry(i);
980 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
981 snd_error(UCM, "compound type expected for %s, is %d", id, snd_config_get_type(cfg));
985 err = fcn(uc_mgr, n, data1, data2);
993 static int strip_legacy_dev_index(char *name)
995 char *dot = strchr(name, '.');
998 if (dot[1] != '0' || dot[2] != '\0') {
999 snd_error(UCM, "device name %s contains a '.',"
1000 " and is not legacy foo.0 format", name);
1011 static int parse_device_list(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
1012 struct dev_list *dev_list,
1013 enum dev_list_type type,
1016 struct dev_list_node *sdev;
1018 snd_config_iterator_t i, next;
1022 if (dev_list->type != DEVLIST_NONE) {
1023 snd_error(UCM, "multiple supported or conflicting device lists");
1028 if (snd_config_get_id(cfg, &id) < 0)
1031 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1032 snd_error(UCM, "compound type expected for %s", id);
1036 snd_config_for_each(i, next, cfg) {
1037 n = snd_config_iterator_entry(i);
1039 if (snd_config_get_id(n, &id) < 0)
1042 sdev = calloc(1, sizeof(struct dev_list_node));
1045 err = parse_string_substitute3(uc_mgr, n, &sdev->name);
1050 err = strip_legacy_dev_index(sdev->name);
1056 list_add(&sdev->list, &dev_list->list);
1059 dev_list->type = type;
1064 /* Find a component device by its name, and remove it from machine device
1067 * Component devices are defined by machine components (usually off-soc
1068 * codes or DSP embeded in SoC). Since alsaconf imports their configuration
1069 * files automatically, we don't know which devices are component devices
1070 * until they are referenced by a machine device sequence. So here when we
1071 * find a referenced device, we move it from the machine device list to the
1072 * component device list. Component devices will not be exposed to applications
1073 * by the original API to list devices for backward compatibility. So sound
1074 * servers can only see the machine devices.
1076 struct use_case_device *find_component_dev(snd_use_case_mgr_t *uc_mgr,
1079 struct list_head *pos, *posdev, *_posdev;
1080 struct use_case_verb *verb;
1081 struct use_case_device *dev;
1083 list_for_each(pos, &uc_mgr->verb_list) {
1084 verb = list_entry(pos, struct use_case_verb, list);
1086 /* search in the component device list */
1087 list_for_each(posdev, &verb->cmpt_device_list) {
1088 dev = list_entry(posdev, struct use_case_device, list);
1089 if (!strcmp(dev->name, name))
1093 /* search the machine device list */
1094 list_for_each_safe(posdev, _posdev, &verb->device_list) {
1095 dev = list_entry(posdev, struct use_case_device, list);
1096 if (!strcmp(dev->name, name)) {
1097 /* find the component device, move it from the
1098 * machine device list to the component device
1101 list_del(&dev->list);
1102 list_add_tail(&dev->list,
1103 &verb->cmpt_device_list);
1112 /* parse sequence of a component device
1114 * This function will find the component device and mark if its enable or
1115 * disable sequence is needed by its parenet device.
1117 static int parse_component_seq(snd_use_case_mgr_t *uc_mgr,
1118 snd_config_t *n, int enable,
1119 struct component_sequence *cmpt_seq)
1124 err = parse_string_substitute3(uc_mgr, n, &val);
1128 cmpt_seq->device = find_component_dev(uc_mgr, val);
1129 if (!cmpt_seq->device) {
1130 snd_error(UCM, "Cannot find component device %s", val);
1136 /* Parent needs its enable or disable sequence */
1137 cmpt_seq->enable = enable;
1145 * Sequence controls elements are in the following form:-
1148 * cset "element_id_syntax value_syntax"
1150 * exec "any unix command with arguments"
1151 * enadev "component device name"
1152 * disdev "component device name"
1155 * cset "name='Master Playback Switch' 0,0"
1156 * cset "iface=PCM,name='Disable HDMI',index=1 0"
1157 * enadev "rt286:Headphones"
1158 * disdev "rt286:Speaker"
1160 static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
1161 struct list_head *base,
1164 struct sequence_element *curr;
1165 snd_config_iterator_t i, next;
1168 const char *cmd = NULL;
1170 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1171 snd_error(UCM, "compound is expected for sequence definition");
1175 snd_config_for_each(i, next, cfg) {
1178 n = snd_config_iterator_entry(i);
1179 err = snd_config_get_id(n, &id);
1183 if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
1184 snd_error(UCM, "string type is expected for sequence command");
1187 err = snd_config_get_string(n, &cmd);
1193 /* alloc new sequence element */
1194 curr = calloc(1, sizeof(struct sequence_element));
1197 list_add_tail(&curr->list, base);
1199 if (strcmp(cmd, "cdev") == 0) {
1200 curr->type = SEQUENCE_ELEMENT_TYPE_CDEV;
1201 err = parse_string_substitute3(uc_mgr, n, &curr->data.cdev);
1203 snd_error(UCM, "cdev requires a string!");
1209 if (strcmp(cmd, "cset") == 0) {
1210 curr->type = SEQUENCE_ELEMENT_TYPE_CSET;
1212 err = parse_string_substitute3(uc_mgr, n, &curr->data.cset);
1214 snd_error(UCM, "%s requires a string!", cmd);
1220 if (strcmp(cmd, "enadev") == 0 ||
1221 strcmp(cmd, "disdev") == 0) {
1222 /* need to enable or disable a component device */
1223 curr->type = SEQUENCE_ELEMENT_TYPE_CMPT_SEQ;
1224 err = parse_component_seq(uc_mgr, n,
1225 strcmp(cmd, "enadev") == 0,
1226 &curr->data.cmpt_seq);
1228 snd_error(UCM, "%s requires a valid device!", cmd);
1234 if (strcmp(cmd, "enadev2") == 0) {
1235 curr->type = SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ;
1239 if (strcmp(cmd, "disdev2") == 0) {
1240 curr->type = SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_SEQ;
1242 err = parse_string_substitute3(uc_mgr, n, &curr->data.device);
1244 snd_error(UCM, "%s requires a valid device!", cmd);
1250 if (strcmp(cmd, "disdevall") == 0) {
1251 curr->type = SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_ALL;
1255 if (strcmp(cmd, "cset-bin-file") == 0) {
1256 curr->type = SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE;
1260 if (strcmp(cmd, "cset-tlv") == 0) {
1261 curr->type = SEQUENCE_ELEMENT_TYPE_CSET_TLV;
1265 if (strcmp(cmd, "cset-new") == 0) {
1266 curr->type = SEQUENCE_ELEMENT_TYPE_CSET_NEW;
1270 if (strcmp(cmd, "ctl-remove") == 0) {
1271 curr->type = SEQUENCE_ELEMENT_TYPE_CTL_REMOVE;
1275 if (strcmp(cmd, "sysw") == 0) {
1276 curr->type = SEQUENCE_ELEMENT_TYPE_SYSSET;
1277 err = parse_string_substitute3(uc_mgr, n, &curr->data.sysw);
1279 snd_error(UCM, "sysw requires a string!");
1285 if (strcmp(cmd, "usleep") == 0) {
1286 curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
1287 err = parse_integer_substitute3(uc_mgr, n, &curr->data.sleep);
1289 snd_error(UCM, "usleep requires integer!");
1295 if (strcmp(cmd, "msleep") == 0) {
1296 curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
1297 err = parse_integer_substitute3(uc_mgr, n, &curr->data.sleep);
1299 snd_error(UCM, "msleep requires integer!");
1302 curr->data.sleep *= 1000L;
1306 if (strcmp(cmd, "exec") == 0) {
1307 curr->type = SEQUENCE_ELEMENT_TYPE_EXEC;
1309 err = parse_string_substitute3(uc_mgr, n, &curr->data.exec);
1311 snd_error(UCM, "exec requires a string!");
1317 if (strcmp(cmd, "shell") == 0) {
1318 curr->type = SEQUENCE_ELEMENT_TYPE_SHELL;
1322 if (strcmp(cmd, "cfg-save") == 0) {
1323 curr->type = SEQUENCE_ELEMENT_TYPE_CFGSAVE;
1324 err = parse_string_substitute3(uc_mgr, n, &curr->data.cfgsave);
1326 snd_error(UCM, "sysw requires a string!");
1332 if (strcmp(cmd, "comment") == 0)
1335 snd_error(UCM, "sequence command '%s' is ignored", cmd);
1338 list_del(&curr->list);
1339 uc_mgr_free_sequence_element(curr);
1348 int uc_mgr_add_value(struct list_head *base, const char *key, char *val)
1350 struct ucm_value *curr;
1352 curr = calloc(1, sizeof(struct ucm_value));
1355 curr->name = strdup(key);
1356 if (curr->name == NULL) {
1360 list_add_tail(&curr->list, base);
1368 * Parse values describing PCM, control/mixer settings and stream parameters.
1373 * PlaybackVolume "name='Master Playback Volume',index=2"
1374 * PlaybackSwitch "name='Master Playback Switch',index=2"
1377 static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
1378 struct list_head *base,
1381 snd_config_iterator_t i, next;
1384 snd_config_type_t type;
1387 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1388 snd_error(UCM, "compound is expected for value definition");
1392 /* in-place evaluation */
1393 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1397 snd_config_for_each(i, next, cfg) {
1399 n = snd_config_iterator_entry(i);
1400 err = snd_config_get_id(n, &id);
1404 type = snd_config_get_type(n);
1406 case SND_CONFIG_TYPE_INTEGER:
1407 case SND_CONFIG_TYPE_INTEGER64:
1408 case SND_CONFIG_TYPE_REAL:
1409 err = snd_config_get_ascii(n, &s);
1411 snd_error(UCM, "unable to parse value for id '%s': %s!", id, snd_strerror(err));
1415 case SND_CONFIG_TYPE_STRING:
1416 err = parse_string_substitute(uc_mgr, n, &s);
1418 snd_error(UCM, "unable to parse a string for id '%s'!", id);
1423 snd_error(UCM, "invalid type %i in Value compound '%s'", type, id);
1426 err = uc_mgr_add_value(base, id, s);
1437 * Parse Modifier Use cases
1439 * # Each modifier is described in new section. N modifiers are allowed
1440 * SectionModifier."Capture Voice" {
1442 * Comment "Record voice call"
1449 * ConflictingDevice [
1462 * TransitionSequence."ToModifierName" [
1466 * # Optional TQ and ALSA PCMs
1470 * PlaybackVolume "name='Master Playback Volume',index=2"
1471 * PlaybackSwitch "name='Master Playback Switch',index=2"
1475 * SupportedDevice and ConflictingDevice cannot be specified together.
1476 * Both are optional.
1478 static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
1480 void *data1, void *data2)
1482 struct use_case_verb *verb = data1;
1483 struct use_case_modifier *modifier;
1485 snd_config_iterator_t i, next;
1489 if (parse_get_safe_name(uc_mgr, cfg, data2, &name) < 0)
1492 /* allocate modifier */
1493 modifier = calloc(1, sizeof(*modifier));
1494 if (modifier == NULL) {
1498 INIT_LIST_HEAD(&modifier->enable_list);
1499 INIT_LIST_HEAD(&modifier->disable_list);
1500 INIT_LIST_HEAD(&modifier->transition_list);
1501 INIT_LIST_HEAD(&modifier->dev_list.list);
1502 INIT_LIST_HEAD(&modifier->value_list);
1503 list_add_tail(&modifier->list, &verb->modifier_list);
1504 modifier->name = name;
1506 /* in-place evaluation */
1507 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1511 snd_config_for_each(i, next, cfg) {
1513 n = snd_config_iterator_entry(i);
1514 if (snd_config_get_id(n, &id) < 0)
1517 if (strcmp(id, "Comment") == 0) {
1518 err = parse_string_substitute3(uc_mgr, n, &modifier->comment);
1520 snd_error(UCM, "failed to get modifier comment");
1526 if (strcmp(id, "SupportedDevice") == 0) {
1527 err = parse_device_list(uc_mgr, &modifier->dev_list,
1528 DEVLIST_SUPPORTED, n);
1530 snd_error(UCM, "failed to parse supported device list");
1535 if (strcmp(id, "ConflictingDevice") == 0) {
1536 err = parse_device_list(uc_mgr, &modifier->dev_list,
1537 DEVLIST_CONFLICTING, n);
1539 snd_error(UCM, "failed to parse conflicting device list");
1544 if (strcmp(id, "EnableSequence") == 0) {
1545 err = parse_sequence(uc_mgr, &modifier->enable_list, n);
1547 snd_error(UCM, "failed to parse modifier enable sequence");
1553 if (strcmp(id, "DisableSequence") == 0) {
1554 err = parse_sequence(uc_mgr, &modifier->disable_list, n);
1556 snd_error(UCM, "failed to parse modifier disable sequence");
1562 if (strcmp(id, "TransitionSequence") == 0) {
1563 err = parse_transition(uc_mgr, &modifier->transition_list, n);
1565 snd_error(UCM, "failed to parse transition modifier");
1571 if (strcmp(id, "Value") == 0) {
1572 err = parse_value(uc_mgr, &modifier->value_list, n);
1574 snd_error(UCM, "failed to parse Value");
1585 * Parse device configuration fields
1587 static int parse_device_fields(snd_use_case_mgr_t *uc_mgr,
1589 struct use_case_device *device)
1591 snd_config_iterator_t i, next;
1595 snd_config_for_each(i, next, cfg) {
1597 n = snd_config_iterator_entry(i);
1598 if (snd_config_get_id(n, &id) < 0)
1601 if (strcmp(id, "Comment") == 0) {
1602 err = parse_string_substitute3(uc_mgr, n, &device->comment);
1604 snd_error(UCM, "failed to get device comment");
1610 if (strcmp(id, "SupportedDevice") == 0) {
1611 err = parse_device_list(uc_mgr, &device->dev_list,
1612 DEVLIST_SUPPORTED, n);
1614 snd_error(UCM, "failed to parse supported device list");
1620 if (strcmp(id, "ConflictingDevice") == 0) {
1621 err = parse_device_list(uc_mgr, &device->dev_list,
1622 DEVLIST_CONFLICTING, n);
1624 snd_error(UCM, "failed to parse conflicting device list");
1630 if (strcmp(id, "EnableSequence") == 0) {
1631 err = parse_sequence(uc_mgr, &device->enable_list, n);
1633 snd_error(UCM, "failed to parse device enable sequence");
1640 if (strcmp(id, "DisableSequence") == 0) {
1641 err = parse_sequence(uc_mgr, &device->disable_list, n);
1643 snd_error(UCM, "failed to parse device disable sequence");
1650 if (strcmp(id, "TransitionSequence") == 0) {
1651 err = parse_transition(uc_mgr, &device->transition_list, n);
1653 snd_error(UCM, "failed to parse transition device");
1660 if (strcmp(id, "Value") == 0) {
1661 err = parse_value(uc_mgr, &device->value_list, n);
1663 snd_error(UCM, "failed to parse Value");
1673 * Helper function to copy, evaluate and optionally merge configuration trees.
1675 static int uc_mgr_config_copy_eval_merge(snd_use_case_mgr_t *uc_mgr,
1678 snd_config_t *merge_from)
1680 snd_config_t *tmp = NULL;
1683 err = snd_config_copy(&tmp, src);
1687 err = uc_mgr_evaluate_inplace(uc_mgr, tmp);
1689 snd_config_delete(tmp);
1694 err = uc_mgr_config_tree_merge(uc_mgr, tmp, merge_from, NULL, NULL);
1696 snd_config_delete(tmp);
1706 * Parse Device Use Cases
1708 * # Each device is described in new section. N devices are allowed
1709 * SectionDevice."Headphones" {
1710 * Comment "Headphones connected to 3.5mm jack"
1717 * ConflictingDevice [
1730 * TransitionSequence."ToDevice" [
1735 * PlaybackVolume "name='Master Playback Volume',index=2"
1736 * PlaybackSwitch "name='Master Playback Switch',index=2"
1741 static int parse_device_by_name(snd_use_case_mgr_t *uc_mgr,
1743 struct use_case_verb *verb,
1745 struct use_case_device **ret_device)
1747 struct use_case_device *device;
1750 device = calloc(1, sizeof(*device));
1754 INIT_LIST_HEAD(&device->enable_list);
1755 INIT_LIST_HEAD(&device->disable_list);
1756 INIT_LIST_HEAD(&device->transition_list);
1757 INIT_LIST_HEAD(&device->dev_list.list);
1758 INIT_LIST_HEAD(&device->value_list);
1759 INIT_LIST_HEAD(&device->variants);
1760 INIT_LIST_HEAD(&device->variant_list);
1761 list_add_tail(&device->list, &verb->device_list);
1762 device->name = strdup(name);
1763 if (device->name == NULL) {
1767 device->orig_name = strdup(name);
1768 if (device->orig_name == NULL)
1771 err = parse_device_fields(uc_mgr, cfg, device);
1776 *ret_device = device;
1781 static int parse_device(snd_use_case_mgr_t *uc_mgr,
1783 void *data1, void *data2)
1785 struct use_case_verb *verb = data1;
1787 const char *variant_label = NULL;
1788 struct use_case_device *device = NULL;
1789 snd_config_t *primary_cfg_copy = NULL;
1790 snd_config_t *device_variant = NULL;
1791 snd_config_t *merged_cfg = NULL;
1792 snd_config_iterator_t i, next;
1796 if (parse_get_safe_name(uc_mgr, cfg, data2, &name) < 0)
1799 if (uc_mgr->conf_format >= 8 && (colon = strchr(name, ':'))) {
1800 variant_label = colon + 1;
1802 err = snd_config_search(cfg, "DeviceVariant", &device_variant);
1804 snd_config_t *variant_cfg = NULL;
1806 /* Save a copy of the primary config for creating variant devices */
1807 err = snd_config_copy(&primary_cfg_copy, cfg);
1813 err = snd_config_search(device_variant, variant_label, &variant_cfg);
1815 err = uc_mgr_config_copy_eval_merge(uc_mgr, &merged_cfg, cfg, variant_cfg);
1825 /* in-place evaluation */
1826 if (cfg != merged_cfg) {
1827 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1834 err = parse_device_by_name(uc_mgr, cfg, verb, name, &device);
1840 snd_config_delete(merged_cfg);
1844 if (device_variant == NULL)
1847 if (device->dev_list.type == DEVLIST_SUPPORTED) {
1848 snd_error(UCM, "DeviceVariant cannot be used with SupportedDevice");
1853 if (snd_config_get_type(device_variant) != SND_CONFIG_TYPE_COMPOUND) {
1854 snd_error(UCM, "compound type expected for DeviceVariant");
1859 colon = strchr(device->name, ':');
1861 snd_error(UCM, "DeviceVariant requires ':' in device name");
1866 snd_config_for_each(i, next, device_variant) {
1867 const char *variant_name;
1868 char variant_device_name[128];
1869 struct use_case_device *variant = NULL;
1871 n = snd_config_iterator_entry(i);
1873 if (snd_config_get_id(n, &variant_name) < 0)
1876 /* Create variant device name: base:variant_name */
1877 snprintf(variant_device_name, sizeof(variant_device_name),
1878 "%.*s:%s", (int)(colon - device->name),
1879 device->name, variant_name);
1881 err = uc_mgr_config_copy_eval_merge(uc_mgr, &merged_cfg, primary_cfg_copy, n);
1885 err = parse_device_by_name(uc_mgr, merged_cfg, verb,
1886 variant_device_name, &variant);
1887 snd_config_delete(merged_cfg);
1892 /* Link variant to primary device */
1893 list_add(&variant->variant_list, &device->variants);
1895 err = uc_mgr_put_to_dev_list(&device->dev_list, variant->name);
1898 if (device->dev_list.type == DEVLIST_NONE)
1899 device->dev_list.type = DEVLIST_CONFLICTING;
1906 snd_config_delete(merged_cfg);
1907 if (primary_cfg_copy)
1908 snd_config_delete(primary_cfg_copy);
1913 * Parse Device Rename/Delete Command
1915 * # The devices might be renamed to allow the better conditional runtime
1916 * # evaluation. Bellow example renames Speaker1 device to Speaker and
1917 * # removes Speaker2 device.
1918 * RenameDevice."Speaker1" "Speaker"
1919 * RemoveDevice."Speaker2" "Speaker2"
1921 static int parse_dev_name_list(snd_use_case_mgr_t *uc_mgr,
1923 struct list_head *list)
1926 snd_config_iterator_t i, next;
1927 const char *id, *name1;
1928 char *name1s, *name2;
1929 struct ucm_dev_name *dev;
1930 snd_config_iterator_t pos;
1933 if (snd_config_get_id(cfg, &id) < 0)
1936 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1937 snd_error(UCM, "compound type expected for %s", id);
1941 snd_config_for_each(i, next, cfg) {
1942 n = snd_config_iterator_entry(i);
1944 if (snd_config_get_id(n, &name1) < 0)
1947 err = get_string3(uc_mgr, name1, &name1s);
1951 err = parse_string_substitute3(uc_mgr, n, &name2);
1954 snd_error(UCM, "failed to get target device name for '%s'", name1);
1958 /* skip duplicates */
1959 list_for_each(pos, list) {
1960 dev = list_entry(pos, struct ucm_dev_name, list);
1961 if (strcmp(dev->name1, name1s) == 0) {
1970 dev = calloc(1, sizeof(*dev));
1975 dev->name1 = strdup(name1);
1976 if (dev->name1 == NULL) {
1982 list_add_tail(&dev->list, list);
1988 static int parse_compound_check_legacy(snd_use_case_mgr_t *uc_mgr,
1990 int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
1993 const char *id, *idchild;
1994 int child_ctr = 0, legacy_format = 1;
1995 snd_config_iterator_t i, next;
1996 snd_config_t *child;
1999 err = snd_config_get_id(cfg, &id);
2003 snd_config_for_each(i, next, cfg) {
2005 if (child_ctr > 1) {
2009 child = snd_config_iterator_entry(i);
2011 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
2016 if (snd_config_get_id(child, &idchild) < 0)
2019 if (strcmp(idchild, "0")) {
2024 if (child_ctr != 1) {
2029 return parse_compound(uc_mgr, cfg, fcn, data1, (void *)id);
2031 return fcn(uc_mgr, cfg, data1, NULL);
2034 static int parse_device_name(snd_use_case_mgr_t *uc_mgr,
2037 void *data2 ATTRIBUTE_UNUSED)
2039 return parse_compound_check_legacy(uc_mgr, cfg, parse_device, data1);
2042 static int parse_modifier_name(snd_use_case_mgr_t *uc_mgr,
2045 void *data2 ATTRIBUTE_UNUSED)
2047 return parse_compound_check_legacy(uc_mgr, cfg, parse_modifier, data1);
2050 static int verb_dev_list_add(struct use_case_verb *verb,
2051 enum dev_list_type dst_type,
2055 struct use_case_device *device;
2056 struct list_head *pos;
2058 list_for_each(pos, &verb->device_list) {
2059 device = list_entry(pos, struct use_case_device, list);
2061 if (strcmp(device->name, dst) != 0)
2063 if (device->dev_list.type != dst_type) {
2064 if (list_empty(&device->dev_list.list)) {
2065 device->dev_list.type = dst_type;
2067 snd_error(UCM, "incompatible device list type ('%s', '%s')", device->orig_name, src);
2071 return uc_mgr_put_to_dev_list(&device->dev_list, src);
2073 snd_error(UCM, "unable to find device '%s'", dst);
2077 static int verb_dev_list_check(struct use_case_verb *verb)
2079 struct list_head *pos, *pos2, *pos3;
2080 struct use_case_device *device, *target_dev;
2081 struct dev_list_node *dlist, *dlist2;
2083 int max_iterations = 100; /* safety limit to prevent infinite loops */
2084 bool added_something;
2086 /* First pass: ensure bidirectional relationships */
2087 list_for_each(pos, &verb->device_list) {
2088 device = list_entry(pos, struct use_case_device, list);
2089 list_for_each(pos2, &device->dev_list.list) {
2090 dlist = list_entry(pos2, struct dev_list_node, list);
2091 err = verb_dev_list_add(verb, device->dev_list.type,
2092 dlist->name, device->name);
2098 /* Second pass: complete other relationships for devices in group.
2099 * For n devices, at most n-1 iterations are needed.
2101 for (iteration = 0; iteration < max_iterations; iteration++) {
2102 added_something = false;
2104 list_for_each(pos, &verb->device_list) {
2105 device = list_entry(pos, struct use_case_device, list);
2107 if (device->dev_list.type == DEVLIST_NONE)
2110 list_for_each(pos2, &device->dev_list.list) {
2111 dlist = list_entry(pos2, struct dev_list_node, list);
2114 list_for_each(pos3, &verb->device_list) {
2115 struct use_case_device *tmp_dev;
2116 tmp_dev = list_entry(pos3, struct use_case_device, list);
2117 if (strcmp(tmp_dev->name, dlist->name) == 0) {
2118 target_dev = tmp_dev;
2126 list_for_each(pos3, &device->dev_list.list) {
2127 dlist2 = list_entry(pos3, struct dev_list_node, list);
2129 if (strcmp(dlist2->name, target_dev->name) == 0)
2132 /* verb_dev_list_add returns 1 if device was added, 0 if already exists */
2133 err = verb_dev_list_add(verb, device->dev_list.type,
2134 target_dev->name, dlist2->name);
2138 added_something = true;
2143 /* If nothing was added in this iteration, we're done */
2144 if (!added_something)
2148 if (iteration >= max_iterations) {
2149 snd_error(UCM, "too many device list iterations for verb '%s'", verb->name);
2157 * Check if a device name is already in use
2159 static int is_device_name_used(struct use_case_verb *verb, const char *name, struct use_case_device *current)
2161 struct list_head *pos;
2162 struct use_case_device *device;
2164 list_for_each(pos, &verb->device_list) {
2165 device = list_entry(pos, struct use_case_device, list);
2166 if (device != current && strcmp(device->name, name) == 0)
2173 * Update all references to a device name in modifiers and other devices.
2174 * This helper function is used when renaming devices to ensure all
2175 * dev_list references are updated accordingly.
2177 static int verb_update_device_references(struct use_case_verb *verb,
2178 const char *old_name,
2179 const char *new_name)
2181 struct list_head *pos, *pos2;
2182 struct use_case_device *device;
2183 struct use_case_modifier *modifier;
2184 struct dev_list_node *dlist;
2187 list_for_each(pos, &verb->modifier_list) {
2188 modifier = list_entry(pos, struct use_case_modifier, list);
2189 list_for_each(pos2, &modifier->dev_list.list) {
2190 dlist = list_entry(pos2, struct dev_list_node, list);
2191 if (strcmp(dlist->name, old_name) == 0) {
2192 name_copy = strdup(new_name);
2193 if (name_copy == NULL)
2196 dlist->name = name_copy;
2201 list_for_each(pos, &verb->device_list) {
2202 device = list_entry(pos, struct use_case_device, list);
2203 list_for_each(pos2, &device->dev_list.list) {
2204 dlist = list_entry(pos2, struct dev_list_node, list);
2205 if (strcmp(dlist->name, old_name) == 0) {
2206 name_copy = strdup(new_name);
2207 if (name_copy == NULL)
2210 dlist->name = name_copy;
2219 * Normalize device names according to use-case.h specification.
2220 * Device names like "HDMI 1" or "Line 1" should be normalized to "HDMI1" and "Line1".
2221 * When device name contains ':', add index and remove everything after ':' (including).
2222 * If final name is already used, retry with higher index.
2223 * Also updates dev_list members in modifiers and devices to reference the normalized names.
2225 static int verb_normalize_device_names(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb)
2227 struct list_head *pos;
2228 struct use_case_device *device;
2229 char *orig_name, *norm_name, *colon;
2233 list_for_each(pos, &verb->device_list) {
2234 device = list_entry(pos, struct use_case_device, list);
2236 orig_name = strdup(device->name);
2237 if (orig_name == NULL)
2240 norm_name = strdup(device->name);
2241 if (norm_name == NULL) {
2246 if (uc_mgr->conf_format < 8)
2249 colon = strchr(norm_name, ':');
2251 if (colon[1] == '\0' || strchr(colon + 1, ' ')) {
2252 snd_error(UCM, "device descriptor cannot be empty or contain spaces '%s'", orig_name);
2259 snprintf(temp, sizeof(temp), "%s%d", norm_name, index);
2260 if (!is_device_name_used(verb, temp, device))
2263 } while (index < 100); /* Safety limit */
2265 snd_error(UCM, "too many device name conflicts for '%s'", orig_name);
2272 err = parse_device_index(&norm_name, &index);
2274 snd_error(UCM, "cannot parse device name '%s'", orig_name);
2283 snprintf(temp, sizeof(temp), "%s%d", norm_name, index);
2287 device->name = strdup(temp);
2288 if (device->name == NULL) {
2293 /* Update all references to the old device name */
2294 err = verb_update_device_references(verb, orig_name, device->name);
2311 * Strip index from single device names.
2312 * According to use-case.h specification, if there is only one device
2313 * with a given base name (e.g., only "HDMI1" and no "HDMI2"), the index
2314 * should be stripped to produce the final name (e.g., "HDMI").
2316 static int verb_strip_single_device_index(struct use_case_verb *verb)
2318 struct list_head *pos, *pos2;
2319 struct use_case_device *device, *device2;
2320 char *base_name, *test_base;
2322 int count, index, test_index, err;
2324 list_for_each(pos, &verb->device_list) {
2325 device = list_entry(pos, struct use_case_device, list);
2327 base_name = strdup(device->name);
2328 if (base_name == NULL)
2331 err = parse_device_index(&base_name, &index);
2342 /* Count how many devices have the same base name */
2344 list_for_each(pos2, &verb->device_list) {
2345 device2 = list_entry(pos2, struct use_case_device, list);
2346 test_base = strdup(device2->name);
2347 if (test_base == NULL) {
2352 err = parse_device_index(&test_base, &test_index);
2353 if (err >= 0 && strcmp(test_base, base_name) == 0)
2360 orig_name = device->name;
2361 device->name = base_name;
2363 err = verb_update_device_references(verb, orig_name, device->name);
2365 device->name = orig_name;
2380 * Determine priority for a device.
2382 * 1. If 'Priority' value exists, use it as the sort key
2383 * 2. If 'PlaybackPriority' value exists, use it as the sort key
2384 * 3. If 'CapturePriority' value exists, use it as the sort key
2385 * 4. Fallback: LONG_MIN (no priority)
2387 static long verb_device_get_priority(struct use_case_device *device)
2389 struct list_head *pos;
2390 struct ucm_value *val;
2391 const char *priority_str = NULL;
2392 long priority = LONG_MIN;
2395 list_for_each(pos, &device->value_list) {
2396 val = list_entry(pos, struct ucm_value, list);
2397 if (strcmp(val->name, "Priority") == 0) {
2398 priority_str = val->data;
2403 if (!priority_str) {
2404 list_for_each(pos, &device->value_list) {
2405 val = list_entry(pos, struct ucm_value, list);
2406 if (strcmp(val->name, "PlaybackPriority") == 0) {
2407 priority_str = val->data;
2413 if (!priority_str) {
2414 list_for_each(pos, &device->value_list) {
2415 val = list_entry(pos, struct ucm_value, list);
2416 if (strcmp(val->name, "CapturePriority") == 0) {
2417 priority_str = val->data;
2424 err = safe_strtol(priority_str, &priority);
2426 priority = LONG_MIN;
2433 * Sort devices based on priority values.
2435 * 1. If 'Priority' value exists, use it as the sort key
2436 * 2. If 'PlaybackPriority' value exists, use it as the sort key
2437 * 3. If 'CapturePriority' value exists, use it as the sort key
2438 * 4. Fallback: use device->name (original) as the sort key
2439 * Higher priority values are placed first in the list.
2441 static int verb_sort_devices(struct use_case_verb *verb)
2443 struct list_head sorted_list;
2444 struct list_head *pos, *npos;
2445 struct use_case_device *device, *insert_dev;
2447 INIT_LIST_HEAD(&sorted_list);
2449 /* First pass: determine and cache priority for all devices */
2450 list_for_each(pos, &verb->device_list) {
2451 device = list_entry(pos, struct use_case_device, list);
2452 device->sort_priority = verb_device_get_priority(device);
2455 /* Move devices from verb->device_list to sorted_list in sorted order */
2456 list_for_each_safe(pos, npos, &verb->device_list) {
2457 device = list_entry(pos, struct use_case_device, list);
2459 /* Remove device from original list */
2460 list_del(&device->list);
2462 /* Find the insertion point in sorted_list */
2463 /* Devices are sorted in descending order of priority (higher priority first) */
2464 /* If priorities are equal or not defined, use device name as key */
2465 if (list_empty(&sorted_list)) {
2466 list_add_tail(&device->list, &sorted_list);
2468 struct list_head *pos2, *insert_pos = &sorted_list;
2469 list_for_each(pos2, &sorted_list) {
2470 insert_dev = list_entry(pos2, struct use_case_device, list);
2472 if (device->sort_priority > insert_dev->sort_priority) {
2475 } else if (device->sort_priority == insert_dev->sort_priority) {
2476 if (strcmp(device->name, insert_dev->name) < 0) {
2483 list_add_tail(&device->list, insert_pos);
2487 /* Move sorted list back to verb->device_list */
2488 list_splice_init(&sorted_list, &verb->device_list);
2493 static int verb_device_management(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb)
2495 struct list_head *pos;
2496 struct ucm_dev_name *dev;
2499 /* rename devices */
2500 list_for_each(pos, &verb->rename_list) {
2501 dev = list_entry(pos, struct ucm_dev_name, list);
2502 err = uc_mgr_rename_device(verb, dev->name1, dev->name2);
2504 snd_error(UCM, "cannot rename device '%s' to '%s'", dev->name1, dev->name2);
2509 /* remove devices */
2510 list_for_each(pos, &verb->remove_list) {
2511 dev = list_entry(pos, struct ucm_dev_name, list);
2512 err = uc_mgr_remove_device(verb, dev->name2);
2514 snd_error(UCM, "cannot remove device '%s'", dev->name2);
2519 /* those lists are no longer used */
2520 uc_mgr_free_dev_name_list(&verb->rename_list);
2521 uc_mgr_free_dev_name_list(&verb->remove_list);
2523 /* strip index from single device names */
2524 if (uc_mgr->conf_format >= 8) {
2525 /* sort devices by priority */
2526 err = verb_sort_devices(verb);
2531 /* normalize device names to remove spaces per use-case.h specification */
2532 err = verb_normalize_device_names(uc_mgr, verb);
2536 /* strip index from single device names */
2537 if (uc_mgr->conf_format >= 8) {
2538 err = verb_strip_single_device_index(verb);
2544 /* handle conflicting/supported lists */
2545 return verb_dev_list_check(verb);
2549 * Parse Verb Section
2551 * # Example Use case verb section for Voice call blah
2552 * # By Joe Blogs <joe@blogs.com>
2555 * # enable and disable sequences are compulsory
2557 * cset "name='Master Playback Switch',index=2 0,0"
2558 * cset "name='Master Playback Volume',index=2 25,25"
2560 * cset "name='Master Playback Switch',index=2 1,1"
2561 * cset "name='Master Playback Volume',index=2 50,50"
2565 * cset "name='Master Playback Switch',index=2 0,0"
2566 * cset "name='Master Playback Volume',index=2 25,25"
2568 * cset "name='Master Playback Switch',index=2 1,1"
2569 * cset "name='Master Playback Volume',index=2 50,50"
2572 * # Optional transition verb
2573 * TransitionSequence."ToCaseName" [
2577 * # Optional TQ and ALSA PCMs
2581 * PlaybackPCM "hw:0"
2585 static int parse_verb(snd_use_case_mgr_t *uc_mgr,
2586 struct use_case_verb *verb,
2589 snd_config_iterator_t i, next;
2593 /* in-place evaluation */
2594 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
2598 /* parse verb section */
2599 snd_config_for_each(i, next, cfg) {
2601 n = snd_config_iterator_entry(i);
2602 if (snd_config_get_id(n, &id) < 0)
2605 if (strcmp(id, "EnableSequence") == 0) {
2606 err = parse_sequence(uc_mgr, &verb->enable_list, n);
2608 snd_error(UCM, "failed to parse verb enable sequence");
2614 if (strcmp(id, "DisableSequence") == 0) {
2615 err = parse_sequence(uc_mgr, &verb->disable_list, n);
2617 snd_error(UCM, "failed to parse verb disable sequence");
2623 if (strcmp(id, "TransitionSequence") == 0) {
2624 snd_debug(UCM, "Parse TransitionSequence");
2625 err = parse_transition(uc_mgr, &verb->transition_list, n);
2627 snd_error(UCM, "failed to parse transition sequence");
2633 if (strcmp(id, "Value") == 0) {
2634 err = parse_value(uc_mgr, &verb->value_list, n);
2645 * Parse a Use case verb configuration.
2647 * This configuration contains the following :-
2648 * o Verb enable and disable sequences.
2649 * o Supported Device enable and disable sequences for verb.
2650 * o Supported Modifier enable and disable sequences for verb
2651 * o Optional QoS for the verb and modifiers.
2652 * o Optional PCM device ID for verb and modifiers
2653 * o Alias kcontrols IDs for master and volumes and mutes.
2655 static int parse_verb_config(snd_use_case_mgr_t *uc_mgr,
2656 const char *use_case_name,
2657 const char *comment,
2661 snd_config_iterator_t i, next;
2663 struct use_case_verb *verb;
2667 verb = calloc(1, sizeof(struct use_case_verb));
2670 INIT_LIST_HEAD(&verb->enable_list);
2671 INIT_LIST_HEAD(&verb->disable_list);
2672 INIT_LIST_HEAD(&verb->transition_list);
2673 INIT_LIST_HEAD(&verb->device_list);
2674 INIT_LIST_HEAD(&verb->cmpt_device_list);
2675 INIT_LIST_HEAD(&verb->modifier_list);
2676 INIT_LIST_HEAD(&verb->value_list);
2677 INIT_LIST_HEAD(&verb->rename_list);
2678 INIT_LIST_HEAD(&verb->remove_list);
2679 list_add_tail(&verb->list, &uc_mgr->verb_list);
2680 if (use_case_name == NULL)
2682 verb->name = strdup(use_case_name);
2683 if (verb->name == NULL)
2686 if (comment != NULL) {
2687 verb->comment = strdup(comment);
2688 if (verb->comment == NULL)
2692 /* in-place evaluation */
2693 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
2697 /* parse master config sections */
2698 snd_config_for_each(i, next, cfg) {
2700 n = snd_config_iterator_entry(i);
2701 if (snd_config_get_id(n, &id) < 0)
2704 /* find verb section and parse it */
2705 if (strcmp(id, "SectionVerb") == 0) {
2706 err = parse_verb(uc_mgr, verb, n);
2708 snd_error(UCM, "%s failed to parse verb", what);
2714 /* find device sections and parse them */
2715 if (strcmp(id, "SectionDevice") == 0) {
2716 err = parse_compound(uc_mgr, n,
2717 parse_device_name, verb, NULL);
2719 snd_error(UCM, "%s failed to parse device", what);
2725 /* find modifier sections and parse them */
2726 if (strcmp(id, "SectionModifier") == 0) {
2727 err = parse_compound(uc_mgr, n,
2728 parse_modifier_name, verb, NULL);
2730 snd_error(UCM, "%s failed to parse modifier", what);
2736 /* device renames */
2737 if (strcmp(id, "RenameDevice") == 0) {
2738 err = parse_dev_name_list(uc_mgr, n, &verb->rename_list);
2740 snd_error(UCM, "%s failed to parse device rename", what);
2747 if (strcmp(id, "RemoveDevice") == 0) {
2748 err = parse_dev_name_list(uc_mgr, n, &verb->remove_list);
2750 snd_error(UCM, "%s failed to parse device remove", what);
2756 /* alsa-lib configuration */
2757 if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) {
2758 err = parse_libconfig(uc_mgr, n);
2760 snd_error(UCM, "%s failed to parse LibConfig", what);
2767 /* use case verb must have at least 1 device */
2768 if (list_empty(&verb->device_list)) {
2769 snd_error(UCM, "no use case device defined");
2773 /* do device rename and delete */
2774 err = verb_device_management(uc_mgr, verb);
2776 snd_error(UCM, "device management error in verb '%s'", verb->name);
2784 * Parse variant information
2786 static int parse_variant(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
2787 char **_vfile, char **_vcomment)
2789 snd_config_iterator_t i, next;
2791 char *file = NULL, *comment = NULL;
2794 /* parse master config sections */
2795 snd_config_for_each(i, next, cfg) {
2797 n = snd_config_iterator_entry(i);
2798 if (snd_config_get_id(n, &id) < 0)
2801 /* get use case verb file name */
2802 if (strcmp(id, "File") == 0) {
2804 err = parse_string_substitute3(uc_mgr, n, &file);
2806 snd_error(UCM, "failed to get File");
2813 /* get optional use case comment */
2814 if (strncmp(id, "Comment", 7) == 0) {
2816 err = parse_string_substitute3(uc_mgr, n, &comment);
2818 snd_error(UCM, "failed to get Comment");
2825 snd_error(UCM, "unknown field '%s' in Variant section", id);
2833 *_vcomment = comment;
2843 * Parse master section for "Use Case" and "File" tags.
2845 static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
2846 void *data1 ATTRIBUTE_UNUSED,
2847 void *data2 ATTRIBUTE_UNUSED)
2849 snd_config_iterator_t i, next;
2850 snd_config_t *n, *variant = NULL, *config = NULL;
2851 char *use_case_name, *file = NULL, *comment = NULL;
2852 bool variant_ok = false;
2855 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
2856 snd_error(UCM, "compound type expected for use case section");
2860 err = parse_get_safe_name(uc_mgr, cfg, NULL, &use_case_name);
2862 snd_error(UCM, "unable to get name for use case section");
2866 /* in-place evaluation */
2867 uc_mgr->parse_master_section = 1;
2868 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
2869 uc_mgr->parse_master_section = 0;
2873 /* parse master config sections */
2874 snd_config_for_each(i, next, cfg) {
2876 n = snd_config_iterator_entry(i);
2877 if (snd_config_get_id(n, &id) < 0)
2880 /* get use case verb file name */
2881 if (strcmp(id, "File") == 0) {
2882 err = parse_string_substitute3(uc_mgr, n, &file);
2884 snd_error(UCM, "failed to get File");
2890 /* get use case verb configuration block (syntax version 8+) */
2891 if (strcmp(id, "Config") == 0) {
2892 if (uc_mgr->conf_format < 8) {
2893 snd_error(UCM, "Config is supported in v8+ syntax");
2897 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
2898 snd_error(UCM, "compound type expected for Config");
2906 /* get optional use case comment */
2907 if (strncmp(id, "Comment", 7) == 0) {
2908 err = parse_string_substitute3(uc_mgr, n, &comment);
2910 snd_error(UCM, "failed to get Comment");
2916 if (uc_mgr->conf_format >= 6 && strcmp(id, "Variant") == 0) {
2917 snd_config_iterator_t i2, next2;
2919 snd_config_for_each(i2, next2, n) {
2922 n2 = snd_config_iterator_entry(i2);
2923 if (snd_config_get_id(n2, &id2) < 0)
2925 err = uc_mgr_evaluate_inplace(uc_mgr, n2);
2928 if (strcmp(use_case_name, id2) == 0)
2934 snd_error(UCM, "unknown field '%s' in SectionUseCase", id);
2937 if (variant && !variant_ok) {
2938 snd_error(UCM, "undefined variant '%s'", use_case_name);
2943 /* check mutual exclusivity of File and Config */
2944 if (file && config) {
2945 snd_error(UCM, "both File and Config specified in SectionUseCase");
2951 snd_debug(UCM, "use_case_name %s file '%s'", use_case_name, file);
2953 /* do we have both use case name and (file or config) ? */
2954 if (!file && !config) {
2955 snd_error(UCM, "use case missing file or config");
2960 /* parse verb from file or config */
2963 /* load config from file */
2964 err = uc_mgr_config_load_file(uc_mgr, file, &cfg, false);
2967 /* parse the config */
2968 err = parse_verb_config(uc_mgr, use_case_name, comment, cfg, file);
2969 snd_config_delete(cfg);
2971 /* inline config - parse directly */
2972 err = parse_verb_config(uc_mgr, use_case_name, comment, config,
2973 comment ? comment : use_case_name);
2976 /* parse variants */
2977 struct list_head orig_variable_list;
2978 snd_config_t *orig_macros = NULL;
2979 int first_iteration = 1;
2981 /* save original variable list */
2982 err = uc_mgr_duplicate_variables(&orig_variable_list, &uc_mgr->variable_list);
2986 /* save original macros */
2987 if (uc_mgr->macros) {
2988 err = snd_config_copy(&orig_macros, uc_mgr->macros);
2990 goto __variant_error;
2993 snd_config_for_each(i, next, variant) {
2994 char *vfile, *vcomment;
2997 /* restore variables and macros for second and later iterations */
2998 if (!first_iteration) {
2999 uc_mgr_free_value(&uc_mgr->variable_list);
3001 err = uc_mgr_duplicate_variables(&uc_mgr->variable_list, &orig_variable_list);
3003 goto __variant_error;
3005 if (uc_mgr->macros) {
3006 snd_config_delete(uc_mgr->macros);
3007 uc_mgr->macros = NULL;
3010 err = snd_config_copy(&uc_mgr->macros, orig_macros);
3012 goto __variant_error;
3015 first_iteration = 0;
3017 n = snd_config_iterator_entry(i);
3018 if (snd_config_get_id(n, &id) < 0)
3020 if (!parse_is_name_safe(id)) {
3022 goto __variant_error;
3024 err = parse_variant(uc_mgr, n, &vfile, &vcomment);
3027 uc_mgr->parse_variant = id;
3028 if (vfile || file) {
3030 const char *fname = vfile ? vfile : file;
3031 /* load config from file */
3032 err = uc_mgr_config_load_file(uc_mgr, fname, &cfg, false);
3034 err = parse_verb_config(uc_mgr, id,
3035 vcomment ? vcomment : comment,
3037 snd_config_delete(cfg);
3040 /* inline config from variant */
3041 err = parse_verb_config(uc_mgr, id,
3042 vcomment ? vcomment : comment,
3044 vcomment ? vcomment : (comment ? comment : id));
3046 uc_mgr->parse_variant = NULL;
3054 uc_mgr_free_value(&orig_variable_list);
3056 snd_config_delete(orig_macros);
3060 free(use_case_name);
3067 * parse controls which should be run only at initial boot (forcefully)
3069 static int parse_controls_fixedboot(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
3073 if (!list_empty(&uc_mgr->fixedboot_list)) {
3074 snd_error(UCM, "FixedBoot list is not empty");
3077 err = parse_sequence(uc_mgr, &uc_mgr->fixedboot_list, cfg);
3079 snd_error(UCM, "Unable to parse FixedBootSequence");
3087 * parse controls which should be run only at initial boot
3089 static int parse_controls_boot(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
3093 if (!list_empty(&uc_mgr->boot_list)) {
3094 snd_error(UCM, "Boot list is not empty");
3097 err = parse_sequence(uc_mgr, &uc_mgr->boot_list, cfg);
3099 snd_error(UCM, "Unable to parse BootSequence");
3109 static int parse_controls(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
3113 if (!list_empty(&uc_mgr->default_list)) {
3114 snd_error(UCM, "Default list is not empty");
3117 err = parse_sequence(uc_mgr, &uc_mgr->default_list, cfg);
3119 snd_error(UCM, "Unable to parse SectionDefaults");
3127 * Each sound card has a master sound card file that lists all the supported
3128 * use case verbs for that sound card. i.e.
3130 * #Example master file for blah sound card
3131 * #By Joe Blogs <joe@bloggs.org>
3133 * Comment "Nice Abstracted Soundcard"
3135 * # The file is divided into Use case sections. One section per use case verb.
3137 * SectionUseCase."Voice Call" {
3138 * File "voice_call_blah"
3139 * Comment "Make a voice phone call."
3142 * SectionUseCase."HiFi" {
3144 * Comment "Play and record HiFi quality Music."
3147 * # Define Value defaults
3150 * PlaybackCTL "hw:CARD=0"
3151 * CaptureCTL "hw:CARD=0"
3154 * # The initial boot (run once) configuration.
3157 * cset "name='Master Playback Switch',index=2 1,1"
3158 * cset "name='Master Playback Volume',index=2 25,25"
3161 * # This file also stores the default sound card state.
3164 * cset "name='Master Mono Playback',index=1 0"
3165 * cset "name='Master Mono Playback Volume',index=1 0"
3166 * cset "name='PCM Switch',index=2 1,1"
3167 * exec "some binary here"
3172 * # End of example file.
3174 static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
3176 snd_config_iterator_t i, next;
3181 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
3182 snd_error(UCM, "compound type expected for master file");
3186 if (uc_mgr->conf_format >= 2) {
3187 err = parse_syntax_field(uc_mgr, cfg, uc_mgr->conf_file_name);
3192 /* in-place evaluation */
3193 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
3197 /* parse ValueGlobals first */
3198 err = snd_config_search(cfg, "ValueGlobals", &n);
3200 err = parse_value(uc_mgr, &uc_mgr->global_value_list, n);
3202 snd_error(UCM, "failed to parse ValueGlobals");
3207 err = uc_mgr_check_value(&uc_mgr->global_value_list, "BootCardGroup");
3209 uc_mgr->card_group = true;
3210 /* if we are in boot, skip the main parsing loop */
3211 if (uc_mgr->in_boot)
3215 /* parse master config sections */
3216 snd_config_for_each(i, next, cfg) {
3218 n = snd_config_iterator_entry(i);
3219 if (snd_config_get_id(n, &id) < 0)
3222 if (strcmp(id, "Comment") == 0) {
3223 err = parse_string_substitute3(uc_mgr, n, &uc_mgr->comment);
3225 snd_error(UCM, "failed to get master comment");
3231 /* find use case section and parse it */
3232 if (strcmp(id, "SectionUseCase") == 0) {
3233 err = parse_compound(uc_mgr, n,
3234 parse_master_section,
3241 /* find default control values section (force boot sequence only) */
3242 if (strcmp(id, "FixedBootSequence") == 0) {
3243 err = parse_controls_fixedboot(uc_mgr, n);
3249 /* find default control values section (first boot only) */
3250 if (strcmp(id, "BootSequence") == 0) {
3251 err = parse_controls_boot(uc_mgr, n);
3257 /* find default control values section and parse it */
3258 if (strcmp(id, "SectionDefaults") == 0) {
3259 err = parse_controls(uc_mgr, n);
3265 /* ValueDefaults is now parsed at the top of this function */
3266 if (strcmp(id, "ValueDefaults") == 0) {
3267 err = parse_value(uc_mgr, &uc_mgr->value_list, n);
3269 snd_error(UCM, "failed to parse ValueDefaults");
3275 /* ValueGlobals is parsed at the top of this function */
3276 if (strcmp(id, "ValueGlobals") == 0)
3279 /* alsa-lib configuration */
3280 if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) {
3281 err = parse_libconfig(uc_mgr, n);
3283 snd_error(UCM, "failed to parse LibraryConfig");
3290 if (strcmp(id, "Error") == 0)
3291 return error_node(uc_mgr, n);
3293 /* skip further Syntax value updates (Include) */
3294 if (strcmp(id, "Syntax") == 0)
3297 snd_error(UCM, "unknown master file field %s", id);
3302 /* get the card info */
3303 static int get_card_info(snd_use_case_mgr_t *mgr,
3304 const char *ctl_name,
3305 snd_ctl_card_info_t **info)
3307 struct ctl_list *ctl_list;
3310 err = uc_mgr_open_ctl(mgr, &ctl_list, ctl_name, 0);
3315 *info = ctl_list->ctl_info;
3319 /* find the card in the local machine */
3320 static int get_by_card_name(snd_use_case_mgr_t *mgr, const char *card_name)
3323 snd_ctl_card_info_t *info;
3324 const char *_driver, *_name, *_long_name;
3326 snd_ctl_card_info_alloca(&info);
3329 if (snd_card_next(&card) < 0 || card < 0) {
3330 snd_error(UCM, "no soundcards found...");
3337 /* clear the list, keep the only one CTL device */
3338 uc_mgr_free_ctl_list(mgr);
3340 sprintf(name, "hw:%d", card);
3341 err = get_card_info(mgr, name, &info);
3344 _driver = snd_ctl_card_info_get_driver(info);
3345 _name = snd_ctl_card_info_get_name(info);
3346 _long_name = snd_ctl_card_info_get_longname(info);
3347 if (!strcmp(card_name, _driver) ||
3348 !strcmp(card_name, _name) ||
3349 !strcmp(card_name, _long_name))
3353 if (snd_card_next(&card) < 0) {
3354 snd_error(UCM, "snd_card_next");
3359 uc_mgr_free_ctl_list(mgr);
3364 /* set the driver name and long name by the card ctl name */
3365 static inline int get_by_card(snd_use_case_mgr_t *mgr, const char *ctl_name)
3367 return get_card_info(mgr, ctl_name, NULL);
3370 static int parse_toplevel_path(snd_use_case_mgr_t *uc_mgr,
3374 snd_config_iterator_t i, next, i2, next2;
3375 snd_config_t *n, *n2;
3377 char *dir = NULL, *file = NULL, fn[PATH_MAX];
3382 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
3383 snd_error(UCM, "compound type expected for UseCasePath node");
3387 /* parse use case path config sections */
3388 snd_config_for_each(i, next, cfg) {
3389 n = snd_config_iterator_entry(i);
3391 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
3392 snd_error(UCM, "compound type expected for UseCasePath.something node");
3396 if (snd_config_get_id(n, &id) < 0)
3401 /* parse use case path config sections */
3402 snd_config_for_each(i2, next2, n) {
3404 n2 = snd_config_iterator_entry(i2);
3405 if (snd_config_get_id(n2, &id) < 0)
3408 if (strcmp(id, "Version") == 0) {
3409 err = parse_integer_substitute(uc_mgr, n2, &version);
3411 snd_error(UCM, "unable to parse UcmDirectory");
3414 if (version < 1 || version > 2) {
3415 snd_error(UCM, "Version must be 1 or 2");
3422 if (strcmp(id, "Directory") == 0) {
3423 err = parse_string_substitute(uc_mgr, n2, &dir);
3425 snd_error(UCM, "unable to parse Directory");
3431 if (strcmp(id, "File") == 0) {
3432 err = parse_string_substitute(uc_mgr, n2, &file);
3434 snd_error(UCM, "unable to parse File");
3440 snd_error(UCM, "unknown UseCasePath field %s", id);
3444 snd_error(UCM, "Directory is not defined in %s!", filename);
3448 snd_error(UCM, "File is not defined in %s!", filename);
3452 if (ucm_filename(fn, sizeof(fn), version, dir, file) < 0)
3454 snd_trace(UCM, "probing configuration file '%s'", fn);
3455 if (access(fn, R_OK) == 0 && lstat64(fn, &st) == 0) {
3456 if (S_ISLNK(st.st_mode)) {
3458 char *link, *dir2, *p;
3460 link = malloc(PATH_MAX);
3463 r = readlink(fn, link, PATH_MAX - 1);
3469 p = strrchr(link, '/');
3472 dir2 = malloc(PATH_MAX);
3477 strncpy(dir2, dir, PATH_MAX - 1);
3478 strncat(dir2, "/", PATH_MAX - 1);
3479 strncat(dir2, link, PATH_MAX - 1);
3480 fn[PATH_MAX - 1] = '\0';
3486 snd_trace(UCM, "using directory '%s' and file '%s'", dir, file);
3487 if (replace_string(&uc_mgr->conf_dir_name, dir) == NULL)
3489 if (replace_string(&uc_mgr->conf_file_name, file) == NULL)
3491 strncpy(filename, fn, PATH_MAX);
3492 filename[PATH_MAX - 1] = '\0';
3493 uc_mgr->conf_format = version;
3521 static int parse_toplevel_config(snd_use_case_mgr_t *uc_mgr,
3525 snd_config_iterator_t i, next;
3530 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
3531 snd_error(UCM, "compound type expected for toplevel file");
3535 err = parse_syntax_field(uc_mgr, cfg, filename);
3539 /* in-place evaluation */
3540 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
3544 /* parse toplevel config sections */
3545 snd_config_for_each(i, next, cfg) {
3547 n = snd_config_iterator_entry(i);
3548 if (snd_config_get_id(n, &id) < 0)
3551 if (strcmp(id, "UseCasePath") == 0) {
3552 err = parse_toplevel_path(uc_mgr, filename, n);
3558 /* alsa-lib configuration */
3559 if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) {
3560 err = parse_libconfig(uc_mgr, n);
3562 snd_error(UCM, "failed to parse LibConfig");
3568 /* skip further Syntax value updates (Include) */
3569 if (strcmp(id, "Syntax") == 0)
3572 snd_error(UCM, "unknown toplevel field %s", id);
3578 static int load_toplevel_config(snd_use_case_mgr_t *uc_mgr,
3581 char filename[PATH_MAX];
3585 ucm_filename(filename, sizeof(filename), 2, NULL, "ucm.conf");
3587 if (access(filename, R_OK) != 0) {
3588 snd_error(UCM, "Unable to find the top-level configuration file '%s'.", filename);
3592 err = uc_mgr_config_load(2, filename, &tcfg, false);
3596 /* filename is shared for function input and output! */
3597 err = parse_toplevel_config(uc_mgr, filename, tcfg);
3598 snd_config_delete(tcfg);
3602 err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg, false);
3604 snd_error(UCM, "could not parse configuration for card %s", uc_mgr->card_name);
3614 /* load master use case file for sound card based on rules in ucm2/ucm.conf
3616 int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
3622 err = snd_config_top(&uc_mgr->local_config);
3626 err = snd_config_top(&uc_mgr->macros);
3630 name = uc_mgr->card_name;
3631 if (strncmp(name, "hw:", 3) == 0) {
3632 err = get_by_card(uc_mgr, name);
3634 snd_error(UCM, "card '%s' is not valid", name);
3637 } else if (strncmp(name, "strict:", 7)) {
3638 /* do not handle the error here */
3639 /* we can refer the virtual UCM config */
3640 get_by_card_name(uc_mgr, name);
3643 err = load_toplevel_config(uc_mgr, &cfg);
3647 err = parse_master_file(uc_mgr, cfg);
3648 if (uc_mgr->macros) {
3649 snd_config_delete(uc_mgr->macros);
3650 uc_mgr->macros = NULL;
3652 snd_config_delete(cfg);
3654 uc_mgr_free_ctl_list(uc_mgr);
3655 uc_mgr_free_verb(uc_mgr);
3661 uc_mgr_free_ctl_list(uc_mgr);
3662 replace_string(&uc_mgr->conf_dir_name, NULL);
3666 static int filename_filter(const struct dirent64 *dirent)
3670 if (dirent->d_type == DT_DIR) {
3671 if (dirent->d_name[0] == '.') {
3672 if (dirent->d_name[1] == '\0')
3674 if (dirent->d_name[1] == '.' &&
3675 dirent->d_name[2] == '\0')
3683 /* scan all cards and comments
3685 * Cards are defined by machines. Each card/machine installs its UCM
3686 * configuration files in a subdirectory with the same name as the sound
3687 * card under /usr/share/alsa/ucm2. This function will scan all the card
3688 * directories and skip the component directories defined in the array
3691 int uc_mgr_scan_master_configs(const char **_list[])
3693 char filename[PATH_MAX], dfl[PATH_MAX], fn[FILENAME_MAX];
3694 char *env = getenv(ALSA_CONFIG_UCM2_VAR);
3695 snd_use_case_mgr_t *uc_mgr;
3696 const char **list, *d_name;
3698 snd_config_t *cfg, *c;
3699 int i, j, cnt, err, cards;
3702 struct dirent64 **namelist;
3707 err = snd_card_next(&i);
3714 cards += 4; /* plug-and-play */
3717 snprintf(filename, sizeof(filename), "%s/conf.virt.d", env);
3719 snprintf(filename, sizeof(filename), "%s/ucm2/conf.virt.d",
3720 snd_config_topdir());
3722 #if defined(_GNU_SOURCE) && \
3723 !defined(__NetBSD__) && \
3724 !defined(__FreeBSD__) && \
3725 !defined(__OpenBSD__) && \
3726 !defined(__DragonFly__) && \
3727 !defined(__sun) && \
3728 !defined(__ANDROID__) && \
3730 #define SORTFUNC versionsort64
3732 #define SORTFUNC alphasort64
3734 err = scandir64(filename, &namelist, filename_filter, SORTFUNC);
3737 snd_error(UCM, "could not scan directory %s: %s", filename, strerror(-err));
3743 if (strlen(filename) + 8 < sizeof(filename)) {
3744 strcat(filename, "/default");
3745 ss = readlink(filename, dfl, sizeof(dfl)-1);
3748 dfl[sizeof(dfl)-1] = '\0';
3749 if (dfl[0] && dfl[strlen(dfl)-1] == '/')
3750 dfl[strlen(dfl)-1] = '\0';
3757 list = calloc(1, (cards + cnt) * 2 * sizeof(char *));
3764 while (j / 2 < cards) {
3765 err = snd_card_next(&i);
3770 snprintf(fn, sizeof(fn), "-hw:%d", i);
3771 err = snd_use_case_mgr_open(&uc_mgr, fn);
3772 if (err == -ENOENT || err == -ENXIO)
3775 snd_error(UCM, "Unable to open '%s': %s", fn, snd_strerror(err));
3778 err = snd_use_case_get(uc_mgr, "comment", (const char **)&s);
3780 err = snd_card_get_longname(i, &s);
3784 snd_use_case_mgr_close(uc_mgr);
3785 list[j] = strdup(fn + 1);
3786 if (list[j] == NULL) {
3795 for (i = 0; i < cnt; i++) {
3797 d_name = namelist[i]->d_name;
3799 snprintf(fn, sizeof(fn), "%s.conf", d_name);
3800 if (ucm_filename(filename, sizeof(filename), 2, d_name, fn) < 0)
3803 if (eaccess(filename, R_OK))
3805 if (access(filename, R_OK))
3809 err = uc_mgr_config_load(2, filename, &cfg, false);
3812 err = snd_config_search(cfg, "Syntax", &c);
3814 snd_error(UCM, "Syntax field not found in %s", d_name);
3815 snd_config_delete(cfg);
3818 err = snd_config_get_integer(c, &l);
3820 snd_error(UCM, "Syntax field is invalid in %s", d_name);
3821 snd_config_delete(cfg);
3824 if (l < 2 || l > SYNTAX_VERSION_MAX) {
3825 snd_error(UCM, "Incompatible syntax %d in %s", l, d_name);
3826 snd_config_delete(cfg);
3829 err = snd_config_search(cfg, "Comment", &c);
3831 err = parse_string(c, (char **)&list[j+1]);
3833 snd_config_delete(cfg);
3837 snd_config_delete(cfg);
3838 list[j] = strdup(d_name);
3839 if (list[j] == NULL) {
3843 if (strcmp(dfl, list[j]) == 0) {
3844 /* default to top */
3845 const char *save1 = list[j];
3846 const char *save2 = list[j + 1];
3847 memmove(list + 2, list, j * sizeof(char *));
3856 for (i = 0; i < cnt; i++)
3860 for (i = 0; i < j; i++) {
3861 free((void *)list[i * 2]);
3862 free((void *)list[i * 2 + 1]);