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)
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);
79 snd_error(UCM, "failed to open file %s: %d", filename, err);
86 * Replace mallocated string
88 static char *replace_string(char **dst, const char *value)
91 *dst = value ? strdup(value) : NULL;
98 static int parse_string(snd_config_t *n, char **res)
102 err = snd_config_get_string(n, (const char **)res);
112 * Parse string and substitute
114 static int parse_string_substitute(snd_use_case_mgr_t *uc_mgr,
115 snd_config_t *n, char **res)
121 err = snd_config_get_string(n, &str);
124 err = uc_mgr_get_substituted_value(uc_mgr, &s, str);
131 * Parse string and substitute
133 static int parse_string_substitute3(snd_use_case_mgr_t *uc_mgr,
134 snd_config_t *n, char **res)
136 if (uc_mgr->conf_format < 3)
137 return parse_string(n, res);
138 return parse_string_substitute(uc_mgr, n, res);
142 * Parse integer with substitution
144 static int parse_integer_substitute(snd_use_case_mgr_t *uc_mgr,
145 snd_config_t *n, long *res)
150 err = snd_config_get_ascii(n, &s1);
153 err = uc_mgr_get_substituted_value(uc_mgr, &s2, s1);
155 err = safe_strtol(s2, res);
162 * Parse integer with substitution
164 static int parse_integer_substitute3(snd_use_case_mgr_t *uc_mgr,
165 snd_config_t *n, long *res)
170 err = snd_config_get_ascii(n, &s1);
173 if (uc_mgr->conf_format < 3)
176 err = uc_mgr_get_substituted_value(uc_mgr, &s2, s1);
178 err = safe_strtol(s2, res);
188 static int parse_is_name_safe(const char *name)
190 if (strchr(name, '.')) {
191 snd_error(UCM, "char '.' not allowed in '%s'", name);
197 static int get_string3(snd_use_case_mgr_t *uc_mgr, const char *s1, char **s)
199 if (uc_mgr->conf_format < 3) {
205 return uc_mgr_get_substituted_value(uc_mgr, s, s1);
208 static int parse_get_safe_name(snd_use_case_mgr_t *uc_mgr, snd_config_t *n,
209 const char *alt, char **name)
217 err = snd_config_get_id(n, &id);
221 err = get_string3(uc_mgr, id, name);
224 if (!parse_is_name_safe(*name)) {
232 * Parse device index from device name
233 * According to use-case.h specification, device names can have numeric suffixes
234 * like HDMI1, HDMI2, or "Line 1", "Line 2".
235 * This function extracts the index and modifies the name to contain only the base.
237 static int parse_device_index(char **name, int *index)
244 if (!name || !*name || !index)
251 /* Start from the end and find where digits begin */
254 /* Skip trailing digits */
255 while (p > *name && isdigit(*p))
258 /* If no digits found at the end, index is 0 (no index) */
259 if (p == *name + len - 1) {
264 /* Move to first digit */
267 /* Check if there's an optional space before the number */
268 if (p > *name && *(p - 1) == ' ')
271 /* Parse the index */
272 num_start = *p == ' ' ? p + 1 : p;
273 err = safe_strtol(num_start, &idx);
275 /* No valid number found */
282 /* Truncate the name to remove the index part */
284 *p = '\0'; /* Remove space and number */
286 *p = '\0'; /* Remove number only */
292 * Handle 'Error' configuration node.
294 static int error_node(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
299 err = parse_string_substitute3(uc_mgr, cfg, &s);
301 snd_error(UCM, "failed to get Error string");
304 if (!uc_mgr->suppress_nodev_errors)
305 snd_error(UCM, "%s", s);
313 static int parse_syntax_field(snd_use_case_mgr_t *uc_mgr,
314 snd_config_t *cfg, const char *filename)
320 err = snd_config_search(cfg, "Syntax", &n);
322 snd_error(UCM, "Syntax field not found in %s", filename);
325 err = snd_config_get_integer(n, &l);
327 snd_error(UCM, "Syntax field is invalid in %s", filename);
330 if (l < 2 || l > SYNTAX_VERSION_MAX) {
331 snd_error(UCM, "Incompatible syntax %ld in %s", l, filename);
334 /* delete this field to optimize strcmp() call in the parsing loop */
335 snd_config_delete(n);
336 uc_mgr->conf_format = l;
341 * Evaluate variable regex definitions (in-place delete)
343 static int evaluate_regex(snd_use_case_mgr_t *uc_mgr,
346 snd_config_iterator_t i, next;
351 err = snd_config_search(cfg, "DefineRegex", &d);
357 if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
358 snd_error(UCM, "compound type expected for DefineRegex");
362 if (uc_mgr->conf_format < 3) {
363 snd_error(UCM, "DefineRegex is supported in v3+ syntax");
367 snd_config_for_each(i, next, d) {
368 n = snd_config_iterator_entry(i);
369 err = snd_config_get_id(n, &id);
373 snd_error(UCM, "value names starting with '@' are reserved for application variables");
376 err = uc_mgr_define_regex(uc_mgr, id, n);
381 snd_config_delete(d);
386 * Evaluate variable definitions (in-place delete)
388 static int evaluate_define(snd_use_case_mgr_t *uc_mgr,
391 snd_config_iterator_t i, next;
397 err = snd_config_search(cfg, "Define", &d);
399 return evaluate_regex(uc_mgr, cfg);
403 if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
404 snd_error(UCM, "compound type expected for Define");
408 if (uc_mgr->conf_format < 3) {
409 snd_error(UCM, "Define is supported in v3+ syntax");
413 snd_config_for_each(i, next, d) {
414 n = snd_config_iterator_entry(i);
415 err = snd_config_get_id(n, &id);
418 err = snd_config_get_ascii(n, &var);
421 err = uc_mgr_get_substituted_value(uc_mgr, &s, var);
427 snd_error(UCM, "value names starting with '@' are reserved for application variables");
430 err = uc_mgr_set_variable(uc_mgr, id, s);
436 snd_config_delete(d);
438 return evaluate_regex(uc_mgr, cfg);
442 * Evaluate macro definitions (in-place delete)
444 static int evaluate_define_macro(snd_use_case_mgr_t *uc_mgr,
450 err = snd_config_search(cfg, "DefineMacro", &d);
456 if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
457 snd_error(UCM, "compound type expected for DefineMacro");
461 if (uc_mgr->conf_format < 6) {
462 snd_error(UCM, "DefineMacro is supported in v6+ syntax");
466 err = snd_config_merge(uc_mgr->macros, d, 0);
473 static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr,
477 snd_config_iterator_t i, next;
478 snd_config_t *m, *mc, *a, *n;
479 const char *mid, *id;
480 char name[128], *var, *var2;
484 err = snd_config_get_id(args, &mid);
487 err = snd_config_search(uc_mgr->macros, mid, &m);
489 snd_error(UCM, "Macro '%s' is not defined", mid);
494 if (snd_config_get_type(args) == SND_CONFIG_TYPE_STRING) {
495 err = snd_config_get_string(args, &s);
498 err = snd_config_load_string(&a, s, 0);
501 } else if (snd_config_get_type(args) != SND_CONFIG_TYPE_COMPOUND) {
506 snd_config_for_each(i, next, a) {
507 n = snd_config_iterator_entry(i);
508 err = snd_config_get_id(n, &id);
511 snprintf(name, sizeof(name), "__%s", id);
512 if (uc_mgr_get_variable(uc_mgr, name)) {
513 snd_error(UCM, "Macro argument '%s' is already defined", name);
516 err = snd_config_get_ascii(n, &var);
519 if (uc_mgr->conf_format < 7) {
520 err = uc_mgr_set_variable(uc_mgr, name, var);
523 err = uc_mgr_get_substituted_value(uc_mgr, &var2, var);
526 err = uc_mgr_set_variable(uc_mgr, name, var2);
534 /* merge + substitute variables */
535 err = snd_config_copy(&mc, m);
538 err = uc_mgr_evaluate_inplace(uc_mgr, mc);
540 snd_config_delete(mc);
543 err = uc_mgr_config_tree_merge(uc_mgr, dst, mc, NULL, NULL);
544 snd_config_delete(mc);
546 /* delete arguments */
547 snd_config_for_each(i, next, a) {
548 n = snd_config_iterator_entry(i);
549 err = snd_config_get_id(n, &id);
552 snprintf(name, sizeof(name), "__%s", id);
553 err = uc_mgr_delete_variable(uc_mgr, name);
560 snd_config_delete(a);
565 * Evaluate macro definitions and instances (in-place delete)
567 static int evaluate_macro(snd_use_case_mgr_t *uc_mgr,
570 snd_config_iterator_t i, i2, next, next2;
571 snd_config_t *d, *n, *n2;
574 ret = evaluate_define_macro(uc_mgr, cfg);
578 err = snd_config_search(cfg, "Macro", &d);
584 if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
585 snd_error(UCM, "compound type expected for DefineMacro");
589 if (uc_mgr->conf_format < 6) {
590 snd_error(UCM, "Macro is supported in v6+ syntax");
594 snd_config_for_each(i, next, d) {
595 n = snd_config_iterator_entry(i);
596 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
598 if (snd_config_get_id(n, &id))
600 snd_error(UCM, "compound type expected for Macro.%s", id);
603 snd_config_for_each(i2, next2, n) {
604 n2 = snd_config_iterator_entry(i2);
605 err = evaluate_macro1(uc_mgr, cfg, n2);
611 snd_config_delete(d);
617 * Evaluate include (in-place)
619 static int evaluate_include(snd_use_case_mgr_t *uc_mgr,
625 err = snd_config_search(cfg, "Include", &n);
631 err = uc_mgr_evaluate_include(uc_mgr, cfg, n);
632 snd_config_delete(n);
637 * Evaluate condition (in-place)
639 static int evaluate_condition(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
644 err = snd_config_search(cfg, "If", &n);
650 err = uc_mgr_evaluate_condition(uc_mgr, cfg, n);
651 snd_config_delete(n);
656 * Evaluate variant (in-place)
658 static int evaluate_variant(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
660 snd_config_iterator_t i, next;
665 err = snd_config_search(cfg, "Variant", &c);
671 if (uc_mgr->conf_format < 6) {
672 snd_error(UCM, "Variant is supported in v6+ syntax");
676 if (uc_mgr->parse_master_section)
679 if (uc_mgr->parse_variant == NULL)
682 snd_config_for_each(i, next, c) {
683 n = snd_config_iterator_entry(i);
685 if (snd_config_get_id(n, &id) < 0)
688 if (strcmp(id, uc_mgr->parse_variant))
691 err = uc_mgr_evaluate_inplace(uc_mgr, n);
695 err = uc_mgr_config_tree_merge(uc_mgr, cfg, n, NULL, NULL);
698 snd_config_delete(c);
703 snd_config_delete(c);
710 int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
713 long iterations = 10000;
714 int err1 = 0, err2 = 0, err3 = 0, err4 = 0, err5 = 0;
716 while (err1 == 0 || err2 == 0 || err3 == 0 || err4 == 0 || err5 == 0) {
717 if (iterations == 0) {
718 snd_error(UCM, "Maximal inplace evaluation iterations number reached (recursive references?)");
722 /* variables at first */
723 err1 = evaluate_define(uc_mgr, cfg);
726 /* include at second */
727 err2 = evaluate_include(uc_mgr, cfg);
730 /* include or macro may define another variables */
731 /* conditions may depend on them */
734 err3 = evaluate_variant(uc_mgr, cfg);
739 uc_mgr->macro_hops++;
740 if (uc_mgr->macro_hops > 100) {
741 snd_error(UCM, "Maximal macro hops reached!");
744 err4 = evaluate_macro(uc_mgr, cfg);
745 uc_mgr->macro_hops--;
750 err5 = evaluate_condition(uc_mgr, cfg);
758 * Parse one item for alsa-lib config
760 static int parse_libconfig1(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
762 snd_config_iterator_t i, next;
763 snd_config_t *n, *config = NULL;
764 const char *id, *file = NULL;
765 bool substfile = false, substconfig = false;
768 if (snd_config_get_id(cfg, &id) < 0)
771 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
772 snd_error(UCM, "compound type expected for %s", id);
776 snd_config_for_each(i, next, cfg) {
777 n = snd_config_iterator_entry(i);
779 if (snd_config_get_id(n, &id) < 0)
782 if (strcmp(id, "File") == 0 ||
783 strcmp(id, "SubstiFile") == 0) {
784 substfile = id[0] == 'S';
785 err = snd_config_get_string(n, &file);
791 if (strcmp(id, "Config") == 0 ||
792 strcmp(id, "SubstiConfig") == 0) {
793 substconfig = id[0] == 'S';
794 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND)
800 snd_error(UCM, "unknown field %s", id);
807 err = uc_mgr_config_load_file(uc_mgr, file, &cfg);
810 err = uc_mgr_substitute_tree(uc_mgr, cfg);
812 snd_config_delete(cfg);
815 err = snd_config_merge(uc_mgr->local_config, cfg, 0);
817 snd_config_delete(cfg);
821 char filename[PATH_MAX];
823 ucm_filename(filename, sizeof(filename), uc_mgr->conf_format,
824 file[0] == '/' ? NULL : uc_mgr->conf_dir_name,
826 err = uc_mgr_config_load_into(uc_mgr->conf_format, filename, uc_mgr->local_config);
834 err = uc_mgr_substitute_tree(uc_mgr, config);
838 err = snd_config_merge(uc_mgr->local_config, config, 0);
847 * Parse alsa-lib config
849 static int parse_libconfig(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
851 snd_config_iterator_t i, next;
856 if (snd_config_get_id(cfg, &id) < 0)
859 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
860 snd_error(UCM, "compound type expected for %s", id);
864 snd_config_for_each(i, next, cfg) {
865 n = snd_config_iterator_entry(i);
867 err = parse_libconfig1(uc_mgr, n);
878 static int parse_transition(snd_use_case_mgr_t *uc_mgr,
879 struct list_head *tlist,
882 struct transition_sequence *tseq;
884 snd_config_iterator_t i, next;
888 if (snd_config_get_id(cfg, &id) < 0)
891 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
892 snd_error(UCM, "compound type expected for %s", id);
896 snd_config_for_each(i, next, cfg) {
897 n = snd_config_iterator_entry(i);
899 if (snd_config_get_id(n, &id) < 0)
902 tseq = calloc(1, sizeof(*tseq));
905 INIT_LIST_HEAD(&tseq->transition_list);
907 err = get_string3(uc_mgr, id, &tseq->name);
913 err = parse_sequence(uc_mgr, &tseq->transition_list, n);
915 uc_mgr_free_transition_element(tseq);
919 list_add(&tseq->list, tlist);
927 static int parse_compound(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
928 int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
929 void *data1, void *data2)
932 snd_config_iterator_t i, next;
936 if (snd_config_get_id(cfg, &id) < 0)
939 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
940 snd_error(UCM, "compound type expected for %s", id);
944 snd_config_for_each(i, next, cfg) {
945 n = snd_config_iterator_entry(i);
947 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
948 snd_error(UCM, "compound type expected for %s, is %d", id, snd_config_get_type(cfg));
952 err = fcn(uc_mgr, n, data1, data2);
960 static int strip_legacy_dev_index(char *name)
962 char *dot = strchr(name, '.');
965 if (dot[1] != '0' || dot[2] != '\0') {
966 snd_error(UCM, "device name %s contains a '.',"
967 " and is not legacy foo.0 format", name);
978 static int parse_device_list(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
979 struct dev_list *dev_list,
980 enum dev_list_type type,
983 struct dev_list_node *sdev;
985 snd_config_iterator_t i, next;
989 if (dev_list->type != DEVLIST_NONE) {
990 snd_error(UCM, "multiple supported or conflicting device lists");
995 if (snd_config_get_id(cfg, &id) < 0)
998 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
999 snd_error(UCM, "compound type expected for %s", id);
1003 snd_config_for_each(i, next, cfg) {
1004 n = snd_config_iterator_entry(i);
1006 if (snd_config_get_id(n, &id) < 0)
1009 sdev = calloc(1, sizeof(struct dev_list_node));
1012 err = parse_string_substitute3(uc_mgr, n, &sdev->name);
1017 err = strip_legacy_dev_index(sdev->name);
1023 list_add(&sdev->list, &dev_list->list);
1026 dev_list->type = type;
1031 /* Find a component device by its name, and remove it from machine device
1034 * Component devices are defined by machine components (usually off-soc
1035 * codes or DSP embeded in SoC). Since alsaconf imports their configuration
1036 * files automatically, we don't know which devices are component devices
1037 * until they are referenced by a machine device sequence. So here when we
1038 * find a referenced device, we move it from the machine device list to the
1039 * component device list. Component devices will not be exposed to applications
1040 * by the original API to list devices for backward compatibility. So sound
1041 * servers can only see the machine devices.
1043 struct use_case_device *find_component_dev(snd_use_case_mgr_t *uc_mgr,
1046 struct list_head *pos, *posdev, *_posdev;
1047 struct use_case_verb *verb;
1048 struct use_case_device *dev;
1050 list_for_each(pos, &uc_mgr->verb_list) {
1051 verb = list_entry(pos, struct use_case_verb, list);
1053 /* search in the component device list */
1054 list_for_each(posdev, &verb->cmpt_device_list) {
1055 dev = list_entry(posdev, struct use_case_device, list);
1056 if (!strcmp(dev->name, name))
1060 /* search the machine device list */
1061 list_for_each_safe(posdev, _posdev, &verb->device_list) {
1062 dev = list_entry(posdev, struct use_case_device, list);
1063 if (!strcmp(dev->name, name)) {
1064 /* find the component device, move it from the
1065 * machine device list to the component device
1068 list_del(&dev->list);
1069 list_add_tail(&dev->list,
1070 &verb->cmpt_device_list);
1079 /* parse sequence of a component device
1081 * This function will find the component device and mark if its enable or
1082 * disable sequence is needed by its parenet device.
1084 static int parse_component_seq(snd_use_case_mgr_t *uc_mgr,
1085 snd_config_t *n, int enable,
1086 struct component_sequence *cmpt_seq)
1091 err = parse_string_substitute3(uc_mgr, n, &val);
1095 cmpt_seq->device = find_component_dev(uc_mgr, val);
1096 if (!cmpt_seq->device) {
1097 snd_error(UCM, "Cannot find component device %s", val);
1103 /* Parent needs its enable or disable sequence */
1104 cmpt_seq->enable = enable;
1112 * Sequence controls elements are in the following form:-
1115 * cset "element_id_syntax value_syntax"
1117 * exec "any unix command with arguments"
1118 * enadev "component device name"
1119 * disdev "component device name"
1122 * cset "name='Master Playback Switch' 0,0"
1123 * cset "iface=PCM,name='Disable HDMI',index=1 0"
1124 * enadev "rt286:Headphones"
1125 * disdev "rt286:Speaker"
1127 static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
1128 struct list_head *base,
1131 struct sequence_element *curr;
1132 snd_config_iterator_t i, next;
1135 const char *cmd = NULL;
1137 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1138 snd_error(UCM, "compound is expected for sequence definition");
1142 snd_config_for_each(i, next, cfg) {
1145 n = snd_config_iterator_entry(i);
1146 err = snd_config_get_id(n, &id);
1150 if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
1151 snd_error(UCM, "string type is expected for sequence command");
1154 err = snd_config_get_string(n, &cmd);
1160 /* alloc new sequence element */
1161 curr = calloc(1, sizeof(struct sequence_element));
1164 list_add_tail(&curr->list, base);
1166 if (strcmp(cmd, "cdev") == 0) {
1167 curr->type = SEQUENCE_ELEMENT_TYPE_CDEV;
1168 err = parse_string_substitute3(uc_mgr, n, &curr->data.cdev);
1170 snd_error(UCM, "cdev requires a string!");
1176 if (strcmp(cmd, "cset") == 0) {
1177 curr->type = SEQUENCE_ELEMENT_TYPE_CSET;
1179 err = parse_string_substitute3(uc_mgr, n, &curr->data.cset);
1181 snd_error(UCM, "%s requires a string!", cmd);
1187 if (strcmp(cmd, "enadev") == 0 ||
1188 strcmp(cmd, "disdev") == 0) {
1189 /* need to enable or disable a component device */
1190 curr->type = SEQUENCE_ELEMENT_TYPE_CMPT_SEQ;
1191 err = parse_component_seq(uc_mgr, n,
1192 strcmp(cmd, "enadev") == 0,
1193 &curr->data.cmpt_seq);
1195 snd_error(UCM, "%s requires a valid device!", cmd);
1201 if (strcmp(cmd, "enadev2") == 0) {
1202 curr->type = SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ;
1206 if (strcmp(cmd, "disdev2") == 0) {
1207 curr->type = SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_SEQ;
1209 err = parse_string_substitute3(uc_mgr, n, &curr->data.device);
1211 snd_error(UCM, "%s requires a valid device!", cmd);
1217 if (strcmp(cmd, "disdevall") == 0) {
1218 curr->type = SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_ALL;
1222 if (strcmp(cmd, "cset-bin-file") == 0) {
1223 curr->type = SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE;
1227 if (strcmp(cmd, "cset-tlv") == 0) {
1228 curr->type = SEQUENCE_ELEMENT_TYPE_CSET_TLV;
1232 if (strcmp(cmd, "cset-new") == 0) {
1233 curr->type = SEQUENCE_ELEMENT_TYPE_CSET_NEW;
1237 if (strcmp(cmd, "ctl-remove") == 0) {
1238 curr->type = SEQUENCE_ELEMENT_TYPE_CTL_REMOVE;
1242 if (strcmp(cmd, "sysw") == 0) {
1243 curr->type = SEQUENCE_ELEMENT_TYPE_SYSSET;
1244 err = parse_string_substitute3(uc_mgr, n, &curr->data.sysw);
1246 snd_error(UCM, "sysw requires a string!");
1252 if (strcmp(cmd, "usleep") == 0) {
1253 curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
1254 err = parse_integer_substitute3(uc_mgr, n, &curr->data.sleep);
1256 snd_error(UCM, "usleep requires integer!");
1262 if (strcmp(cmd, "msleep") == 0) {
1263 curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
1264 err = parse_integer_substitute3(uc_mgr, n, &curr->data.sleep);
1266 snd_error(UCM, "msleep requires integer!");
1269 curr->data.sleep *= 1000L;
1273 if (strcmp(cmd, "exec") == 0) {
1274 curr->type = SEQUENCE_ELEMENT_TYPE_EXEC;
1276 err = parse_string_substitute3(uc_mgr, n, &curr->data.exec);
1278 snd_error(UCM, "exec requires a string!");
1284 if (strcmp(cmd, "shell") == 0) {
1285 curr->type = SEQUENCE_ELEMENT_TYPE_SHELL;
1289 if (strcmp(cmd, "cfg-save") == 0) {
1290 curr->type = SEQUENCE_ELEMENT_TYPE_CFGSAVE;
1291 err = parse_string_substitute3(uc_mgr, n, &curr->data.cfgsave);
1293 snd_error(UCM, "sysw requires a string!");
1299 if (strcmp(cmd, "comment") == 0)
1302 snd_error(UCM, "sequence command '%s' is ignored", cmd);
1305 list_del(&curr->list);
1306 uc_mgr_free_sequence_element(curr);
1315 int uc_mgr_add_value(struct list_head *base, const char *key, char *val)
1317 struct ucm_value *curr;
1319 curr = calloc(1, sizeof(struct ucm_value));
1322 curr->name = strdup(key);
1323 if (curr->name == NULL) {
1327 list_add_tail(&curr->list, base);
1335 * Parse values describing PCM, control/mixer settings and stream parameters.
1340 * PlaybackVolume "name='Master Playback Volume',index=2"
1341 * PlaybackSwitch "name='Master Playback Switch',index=2"
1344 static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
1345 struct list_head *base,
1348 snd_config_iterator_t i, next;
1351 snd_config_type_t type;
1354 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1355 snd_error(UCM, "compound is expected for value definition");
1359 /* in-place evaluation */
1360 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1364 snd_config_for_each(i, next, cfg) {
1366 n = snd_config_iterator_entry(i);
1367 err = snd_config_get_id(n, &id);
1371 type = snd_config_get_type(n);
1373 case SND_CONFIG_TYPE_INTEGER:
1374 case SND_CONFIG_TYPE_INTEGER64:
1375 case SND_CONFIG_TYPE_REAL:
1376 err = snd_config_get_ascii(n, &s);
1378 snd_error(UCM, "unable to parse value for id '%s': %s!", id, snd_strerror(err));
1382 case SND_CONFIG_TYPE_STRING:
1383 err = parse_string_substitute(uc_mgr, n, &s);
1385 snd_error(UCM, "unable to parse a string for id '%s'!", id);
1390 snd_error(UCM, "invalid type %i in Value compound '%s'", type, id);
1393 err = uc_mgr_add_value(base, id, s);
1404 * Parse Modifier Use cases
1406 * # Each modifier is described in new section. N modifiers are allowed
1407 * SectionModifier."Capture Voice" {
1409 * Comment "Record voice call"
1416 * ConflictingDevice [
1429 * TransitionSequence."ToModifierName" [
1433 * # Optional TQ and ALSA PCMs
1437 * PlaybackVolume "name='Master Playback Volume',index=2"
1438 * PlaybackSwitch "name='Master Playback Switch',index=2"
1442 * SupportedDevice and ConflictingDevice cannot be specified together.
1443 * Both are optional.
1445 static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
1447 void *data1, void *data2)
1449 struct use_case_verb *verb = data1;
1450 struct use_case_modifier *modifier;
1452 snd_config_iterator_t i, next;
1456 if (parse_get_safe_name(uc_mgr, cfg, data2, &name) < 0)
1459 /* allocate modifier */
1460 modifier = calloc(1, sizeof(*modifier));
1461 if (modifier == NULL) {
1465 INIT_LIST_HEAD(&modifier->enable_list);
1466 INIT_LIST_HEAD(&modifier->disable_list);
1467 INIT_LIST_HEAD(&modifier->transition_list);
1468 INIT_LIST_HEAD(&modifier->dev_list.list);
1469 INIT_LIST_HEAD(&modifier->value_list);
1470 list_add_tail(&modifier->list, &verb->modifier_list);
1471 modifier->name = name;
1473 /* in-place evaluation */
1474 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1478 snd_config_for_each(i, next, cfg) {
1480 n = snd_config_iterator_entry(i);
1481 if (snd_config_get_id(n, &id) < 0)
1484 if (strcmp(id, "Comment") == 0) {
1485 err = parse_string_substitute3(uc_mgr, n, &modifier->comment);
1487 snd_error(UCM, "failed to get modifier comment");
1493 if (strcmp(id, "SupportedDevice") == 0) {
1494 err = parse_device_list(uc_mgr, &modifier->dev_list,
1495 DEVLIST_SUPPORTED, n);
1497 snd_error(UCM, "failed to parse supported device list");
1502 if (strcmp(id, "ConflictingDevice") == 0) {
1503 err = parse_device_list(uc_mgr, &modifier->dev_list,
1504 DEVLIST_CONFLICTING, n);
1506 snd_error(UCM, "failed to parse conflicting device list");
1511 if (strcmp(id, "EnableSequence") == 0) {
1512 err = parse_sequence(uc_mgr, &modifier->enable_list, n);
1514 snd_error(UCM, "failed to parse modifier enable sequence");
1520 if (strcmp(id, "DisableSequence") == 0) {
1521 err = parse_sequence(uc_mgr, &modifier->disable_list, n);
1523 snd_error(UCM, "failed to parse modifier disable sequence");
1529 if (strcmp(id, "TransitionSequence") == 0) {
1530 err = parse_transition(uc_mgr, &modifier->transition_list, n);
1532 snd_error(UCM, "failed to parse transition modifier");
1538 if (strcmp(id, "Value") == 0) {
1539 err = parse_value(uc_mgr, &modifier->value_list, n);
1541 snd_error(UCM, "failed to parse Value");
1552 * Parse device configuration fields
1554 static int parse_device_fields(snd_use_case_mgr_t *uc_mgr,
1556 struct use_case_device *device)
1558 snd_config_iterator_t i, next;
1562 snd_config_for_each(i, next, cfg) {
1564 n = snd_config_iterator_entry(i);
1565 if (snd_config_get_id(n, &id) < 0)
1568 if (strcmp(id, "Comment") == 0) {
1569 err = parse_string_substitute3(uc_mgr, n, &device->comment);
1571 snd_error(UCM, "failed to get device comment");
1577 if (strcmp(id, "SupportedDevice") == 0) {
1578 err = parse_device_list(uc_mgr, &device->dev_list,
1579 DEVLIST_SUPPORTED, n);
1581 snd_error(UCM, "failed to parse supported device list");
1587 if (strcmp(id, "ConflictingDevice") == 0) {
1588 err = parse_device_list(uc_mgr, &device->dev_list,
1589 DEVLIST_CONFLICTING, n);
1591 snd_error(UCM, "failed to parse conflicting device list");
1597 if (strcmp(id, "EnableSequence") == 0) {
1598 err = parse_sequence(uc_mgr, &device->enable_list, n);
1600 snd_error(UCM, "failed to parse device enable sequence");
1607 if (strcmp(id, "DisableSequence") == 0) {
1608 err = parse_sequence(uc_mgr, &device->disable_list, n);
1610 snd_error(UCM, "failed to parse device disable sequence");
1617 if (strcmp(id, "TransitionSequence") == 0) {
1618 err = parse_transition(uc_mgr, &device->transition_list, n);
1620 snd_error(UCM, "failed to parse transition device");
1627 if (strcmp(id, "Value") == 0) {
1628 err = parse_value(uc_mgr, &device->value_list, n);
1630 snd_error(UCM, "failed to parse Value");
1640 * Helper function to copy, evaluate and optionally merge configuration trees.
1642 static int uc_mgr_config_copy_eval_merge(snd_use_case_mgr_t *uc_mgr,
1645 snd_config_t *merge_from)
1647 snd_config_t *tmp = NULL;
1650 err = snd_config_copy(&tmp, src);
1654 err = uc_mgr_evaluate_inplace(uc_mgr, tmp);
1656 snd_config_delete(tmp);
1661 err = uc_mgr_config_tree_merge(uc_mgr, tmp, merge_from, NULL, NULL);
1663 snd_config_delete(tmp);
1673 * Parse Device Use Cases
1675 * # Each device is described in new section. N devices are allowed
1676 * SectionDevice."Headphones" {
1677 * Comment "Headphones connected to 3.5mm jack"
1684 * ConflictingDevice [
1697 * TransitionSequence."ToDevice" [
1702 * PlaybackVolume "name='Master Playback Volume',index=2"
1703 * PlaybackSwitch "name='Master Playback Switch',index=2"
1708 static int parse_device_by_name(snd_use_case_mgr_t *uc_mgr,
1710 struct use_case_verb *verb,
1712 struct use_case_device **ret_device)
1714 struct use_case_device *device;
1717 device = calloc(1, sizeof(*device));
1721 INIT_LIST_HEAD(&device->enable_list);
1722 INIT_LIST_HEAD(&device->disable_list);
1723 INIT_LIST_HEAD(&device->transition_list);
1724 INIT_LIST_HEAD(&device->dev_list.list);
1725 INIT_LIST_HEAD(&device->value_list);
1726 INIT_LIST_HEAD(&device->variants);
1727 INIT_LIST_HEAD(&device->variant_list);
1728 list_add_tail(&device->list, &verb->device_list);
1729 device->name = strdup(name);
1730 if (device->name == NULL) {
1734 device->orig_name = strdup(name);
1735 if (device->orig_name == NULL)
1738 err = parse_device_fields(uc_mgr, cfg, device);
1743 *ret_device = device;
1748 static int parse_device(snd_use_case_mgr_t *uc_mgr,
1750 void *data1, void *data2)
1752 struct use_case_verb *verb = data1;
1754 const char *variant_label = NULL;
1755 struct use_case_device *device = NULL;
1756 snd_config_t *primary_cfg_copy = NULL;
1757 snd_config_t *device_variant = NULL;
1758 snd_config_t *merged_cfg = NULL;
1759 snd_config_iterator_t i, next;
1763 if (parse_get_safe_name(uc_mgr, cfg, data2, &name) < 0)
1766 if (uc_mgr->conf_format >= 8 && (colon = strchr(name, ':'))) {
1767 variant_label = colon + 1;
1769 err = snd_config_search(cfg, "DeviceVariant", &device_variant);
1771 snd_config_t *variant_cfg = NULL;
1773 /* Save a copy of the primary config for creating variant devices */
1774 err = snd_config_copy(&primary_cfg_copy, cfg);
1780 err = snd_config_search(device_variant, variant_label, &variant_cfg);
1782 err = uc_mgr_config_copy_eval_merge(uc_mgr, &merged_cfg, cfg, variant_cfg);
1792 /* in-place evaluation */
1793 if (cfg != merged_cfg) {
1794 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1801 err = parse_device_by_name(uc_mgr, cfg, verb, name, &device);
1807 snd_config_delete(merged_cfg);
1811 if (device_variant == NULL)
1814 if (device->dev_list.type == DEVLIST_SUPPORTED) {
1815 snd_error(UCM, "DeviceVariant cannot be used with SupportedDevice");
1820 if (snd_config_get_type(device_variant) != SND_CONFIG_TYPE_COMPOUND) {
1821 snd_error(UCM, "compound type expected for DeviceVariant");
1826 colon = strchr(device->name, ':');
1828 snd_error(UCM, "DeviceVariant requires ':' in device name");
1833 snd_config_for_each(i, next, device_variant) {
1834 const char *variant_name;
1835 char variant_device_name[128];
1836 struct use_case_device *variant = NULL;
1838 n = snd_config_iterator_entry(i);
1840 if (snd_config_get_id(n, &variant_name) < 0)
1843 /* Create variant device name: base:variant_name */
1844 snprintf(variant_device_name, sizeof(variant_device_name),
1845 "%.*s:%s", (int)(colon - device->name),
1846 device->name, variant_name);
1848 err = uc_mgr_config_copy_eval_merge(uc_mgr, &merged_cfg, primary_cfg_copy, n);
1852 err = parse_device_by_name(uc_mgr, merged_cfg, verb,
1853 variant_device_name, &variant);
1854 snd_config_delete(merged_cfg);
1859 /* Link variant to primary device */
1860 list_add(&variant->variant_list, &device->variants);
1862 err = uc_mgr_put_to_dev_list(&device->dev_list, variant->name);
1865 if (device->dev_list.type == DEVLIST_NONE)
1866 device->dev_list.type = DEVLIST_CONFLICTING;
1873 snd_config_delete(merged_cfg);
1874 if (primary_cfg_copy)
1875 snd_config_delete(primary_cfg_copy);
1880 * Parse Device Rename/Delete Command
1882 * # The devices might be renamed to allow the better conditional runtime
1883 * # evaluation. Bellow example renames Speaker1 device to Speaker and
1884 * # removes Speaker2 device.
1885 * RenameDevice."Speaker1" "Speaker"
1886 * RemoveDevice."Speaker2" "Speaker2"
1888 static int parse_dev_name_list(snd_use_case_mgr_t *uc_mgr,
1890 struct list_head *list)
1893 snd_config_iterator_t i, next;
1894 const char *id, *name1;
1895 char *name1s, *name2;
1896 struct ucm_dev_name *dev;
1897 snd_config_iterator_t pos;
1900 if (snd_config_get_id(cfg, &id) < 0)
1903 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1904 snd_error(UCM, "compound type expected for %s", id);
1908 snd_config_for_each(i, next, cfg) {
1909 n = snd_config_iterator_entry(i);
1911 if (snd_config_get_id(n, &name1) < 0)
1914 err = get_string3(uc_mgr, name1, &name1s);
1918 err = parse_string_substitute3(uc_mgr, n, &name2);
1921 snd_error(UCM, "failed to get target device name for '%s'", name1);
1925 /* skip duplicates */
1926 list_for_each(pos, list) {
1927 dev = list_entry(pos, struct ucm_dev_name, list);
1928 if (strcmp(dev->name1, name1s) == 0) {
1937 dev = calloc(1, sizeof(*dev));
1942 dev->name1 = strdup(name1);
1943 if (dev->name1 == NULL) {
1949 list_add_tail(&dev->list, list);
1955 static int parse_compound_check_legacy(snd_use_case_mgr_t *uc_mgr,
1957 int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
1960 const char *id, *idchild;
1961 int child_ctr = 0, legacy_format = 1;
1962 snd_config_iterator_t i, next;
1963 snd_config_t *child;
1966 err = snd_config_get_id(cfg, &id);
1970 snd_config_for_each(i, next, cfg) {
1972 if (child_ctr > 1) {
1976 child = snd_config_iterator_entry(i);
1978 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1983 if (snd_config_get_id(child, &idchild) < 0)
1986 if (strcmp(idchild, "0")) {
1991 if (child_ctr != 1) {
1996 return parse_compound(uc_mgr, cfg, fcn, data1, (void *)id);
1998 return fcn(uc_mgr, cfg, data1, NULL);
2001 static int parse_device_name(snd_use_case_mgr_t *uc_mgr,
2004 void *data2 ATTRIBUTE_UNUSED)
2006 return parse_compound_check_legacy(uc_mgr, cfg, parse_device, data1);
2009 static int parse_modifier_name(snd_use_case_mgr_t *uc_mgr,
2012 void *data2 ATTRIBUTE_UNUSED)
2014 return parse_compound_check_legacy(uc_mgr, cfg, parse_modifier, data1);
2017 static int verb_dev_list_add(struct use_case_verb *verb,
2018 enum dev_list_type dst_type,
2022 struct use_case_device *device;
2023 struct list_head *pos;
2025 list_for_each(pos, &verb->device_list) {
2026 device = list_entry(pos, struct use_case_device, list);
2028 if (strcmp(device->name, dst) != 0)
2030 if (device->dev_list.type != dst_type) {
2031 if (list_empty(&device->dev_list.list)) {
2032 device->dev_list.type = dst_type;
2034 snd_error(UCM, "incompatible device list type ('%s', '%s')", device->orig_name, src);
2038 return uc_mgr_put_to_dev_list(&device->dev_list, src);
2040 snd_error(UCM, "unable to find device '%s'", dst);
2044 static int verb_dev_list_check(struct use_case_verb *verb)
2046 struct list_head *pos, *pos2, *pos3;
2047 struct use_case_device *device, *target_dev;
2048 struct dev_list_node *dlist, *dlist2;
2050 int max_iterations = 100; /* safety limit to prevent infinite loops */
2051 bool added_something;
2053 /* First pass: ensure bidirectional relationships */
2054 list_for_each(pos, &verb->device_list) {
2055 device = list_entry(pos, struct use_case_device, list);
2056 list_for_each(pos2, &device->dev_list.list) {
2057 dlist = list_entry(pos2, struct dev_list_node, list);
2058 err = verb_dev_list_add(verb, device->dev_list.type,
2059 dlist->name, device->name);
2065 /* Second pass: complete other relationships for devices in group.
2066 * For n devices, at most n-1 iterations are needed.
2068 for (iteration = 0; iteration < max_iterations; iteration++) {
2069 added_something = false;
2071 list_for_each(pos, &verb->device_list) {
2072 device = list_entry(pos, struct use_case_device, list);
2074 if (device->dev_list.type == DEVLIST_NONE)
2077 list_for_each(pos2, &device->dev_list.list) {
2078 dlist = list_entry(pos2, struct dev_list_node, list);
2081 list_for_each(pos3, &verb->device_list) {
2082 struct use_case_device *tmp_dev;
2083 tmp_dev = list_entry(pos3, struct use_case_device, list);
2084 if (strcmp(tmp_dev->name, dlist->name) == 0) {
2085 target_dev = tmp_dev;
2093 list_for_each(pos3, &device->dev_list.list) {
2094 dlist2 = list_entry(pos3, struct dev_list_node, list);
2096 if (strcmp(dlist2->name, target_dev->name) == 0)
2099 /* verb_dev_list_add returns 1 if device was added, 0 if already exists */
2100 err = verb_dev_list_add(verb, device->dev_list.type,
2101 target_dev->name, dlist2->name);
2105 added_something = true;
2110 /* If nothing was added in this iteration, we're done */
2111 if (!added_something)
2115 if (iteration >= max_iterations) {
2116 snd_error(UCM, "too many device list iterations for verb '%s'", verb->name);
2124 * Check if a device name is already in use
2126 static int is_device_name_used(struct use_case_verb *verb, const char *name, struct use_case_device *current)
2128 struct list_head *pos;
2129 struct use_case_device *device;
2131 list_for_each(pos, &verb->device_list) {
2132 device = list_entry(pos, struct use_case_device, list);
2133 if (device != current && strcmp(device->name, name) == 0)
2140 * Update all references to a device name in modifiers and other devices.
2141 * This helper function is used when renaming devices to ensure all
2142 * dev_list references are updated accordingly.
2144 static int verb_update_device_references(struct use_case_verb *verb,
2145 const char *old_name,
2146 const char *new_name)
2148 struct list_head *pos, *pos2;
2149 struct use_case_device *device;
2150 struct use_case_modifier *modifier;
2151 struct dev_list_node *dlist;
2154 list_for_each(pos, &verb->modifier_list) {
2155 modifier = list_entry(pos, struct use_case_modifier, list);
2156 list_for_each(pos2, &modifier->dev_list.list) {
2157 dlist = list_entry(pos2, struct dev_list_node, list);
2158 if (strcmp(dlist->name, old_name) == 0) {
2159 name_copy = strdup(new_name);
2160 if (name_copy == NULL)
2163 dlist->name = name_copy;
2168 list_for_each(pos, &verb->device_list) {
2169 device = list_entry(pos, struct use_case_device, list);
2170 list_for_each(pos2, &device->dev_list.list) {
2171 dlist = list_entry(pos2, struct dev_list_node, list);
2172 if (strcmp(dlist->name, old_name) == 0) {
2173 name_copy = strdup(new_name);
2174 if (name_copy == NULL)
2177 dlist->name = name_copy;
2186 * Normalize device names according to use-case.h specification.
2187 * Device names like "HDMI 1" or "Line 1" should be normalized to "HDMI1" and "Line1".
2188 * When device name contains ':', add index and remove everything after ':' (including).
2189 * If final name is already used, retry with higher index.
2190 * Also updates dev_list members in modifiers and devices to reference the normalized names.
2192 static int verb_normalize_device_names(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb)
2194 struct list_head *pos;
2195 struct use_case_device *device;
2196 char *orig_name, *norm_name, *colon;
2200 list_for_each(pos, &verb->device_list) {
2201 device = list_entry(pos, struct use_case_device, list);
2203 orig_name = strdup(device->name);
2204 if (orig_name == NULL)
2207 norm_name = strdup(device->name);
2208 if (norm_name == NULL) {
2213 if (uc_mgr->conf_format < 8)
2216 colon = strchr(norm_name, ':');
2218 if (colon[1] == '\0' || strchr(colon + 1, ' ')) {
2219 snd_error(UCM, "device descriptor cannot be empty or contain spaces '%s'", orig_name);
2226 snprintf(temp, sizeof(temp), "%s%d", norm_name, index);
2227 if (!is_device_name_used(verb, temp, device))
2230 } while (index < 100); /* Safety limit */
2232 snd_error(UCM, "too many device name conflicts for '%s'", orig_name);
2239 err = parse_device_index(&norm_name, &index);
2241 snd_error(UCM, "cannot parse device name '%s'", orig_name);
2250 snprintf(temp, sizeof(temp), "%s%d", norm_name, index);
2254 device->name = strdup(temp);
2255 if (device->name == NULL) {
2260 /* Update all references to the old device name */
2261 err = verb_update_device_references(verb, orig_name, device->name);
2278 * Strip index from single device names.
2279 * According to use-case.h specification, if there is only one device
2280 * with a given base name (e.g., only "HDMI1" and no "HDMI2"), the index
2281 * should be stripped to produce the final name (e.g., "HDMI").
2283 static int verb_strip_single_device_index(struct use_case_verb *verb)
2285 struct list_head *pos, *pos2;
2286 struct use_case_device *device, *device2;
2287 char *base_name, *test_base;
2289 int count, index, test_index, err;
2291 list_for_each(pos, &verb->device_list) {
2292 device = list_entry(pos, struct use_case_device, list);
2294 base_name = strdup(device->name);
2295 if (base_name == NULL)
2298 err = parse_device_index(&base_name, &index);
2309 /* Count how many devices have the same base name */
2311 list_for_each(pos2, &verb->device_list) {
2312 device2 = list_entry(pos2, struct use_case_device, list);
2313 test_base = strdup(device2->name);
2314 if (test_base == NULL) {
2319 err = parse_device_index(&test_base, &test_index);
2320 if (err >= 0 && strcmp(test_base, base_name) == 0)
2327 orig_name = device->name;
2328 device->name = base_name;
2330 err = verb_update_device_references(verb, orig_name, device->name);
2332 device->name = orig_name;
2347 * Determine priority for a device.
2349 * 1. If 'Priority' value exists, use it as the sort key
2350 * 2. If 'PlaybackPriority' value exists, use it as the sort key
2351 * 3. If 'CapturePriority' value exists, use it as the sort key
2352 * 4. Fallback: LONG_MIN (no priority)
2354 static long verb_device_get_priority(struct use_case_device *device)
2356 struct list_head *pos;
2357 struct ucm_value *val;
2358 const char *priority_str = NULL;
2359 long priority = LONG_MIN;
2362 list_for_each(pos, &device->value_list) {
2363 val = list_entry(pos, struct ucm_value, list);
2364 if (strcmp(val->name, "Priority") == 0) {
2365 priority_str = val->data;
2370 if (!priority_str) {
2371 list_for_each(pos, &device->value_list) {
2372 val = list_entry(pos, struct ucm_value, list);
2373 if (strcmp(val->name, "PlaybackPriority") == 0) {
2374 priority_str = val->data;
2380 if (!priority_str) {
2381 list_for_each(pos, &device->value_list) {
2382 val = list_entry(pos, struct ucm_value, list);
2383 if (strcmp(val->name, "CapturePriority") == 0) {
2384 priority_str = val->data;
2391 err = safe_strtol(priority_str, &priority);
2393 priority = LONG_MIN;
2400 * Sort devices based on priority values.
2402 * 1. If 'Priority' value exists, use it as the sort key
2403 * 2. If 'PlaybackPriority' value exists, use it as the sort key
2404 * 3. If 'CapturePriority' value exists, use it as the sort key
2405 * 4. Fallback: use device->name (original) as the sort key
2406 * Higher priority values are placed first in the list.
2408 static int verb_sort_devices(struct use_case_verb *verb)
2410 struct list_head sorted_list;
2411 struct list_head *pos, *npos;
2412 struct use_case_device *device, *insert_dev;
2414 INIT_LIST_HEAD(&sorted_list);
2416 /* First pass: determine and cache priority for all devices */
2417 list_for_each(pos, &verb->device_list) {
2418 device = list_entry(pos, struct use_case_device, list);
2419 device->sort_priority = verb_device_get_priority(device);
2422 /* Move devices from verb->device_list to sorted_list in sorted order */
2423 list_for_each_safe(pos, npos, &verb->device_list) {
2424 device = list_entry(pos, struct use_case_device, list);
2426 /* Remove device from original list */
2427 list_del(&device->list);
2429 /* Find the insertion point in sorted_list */
2430 /* Devices are sorted in descending order of priority (higher priority first) */
2431 /* If priorities are equal or not defined, use device name as key */
2432 if (list_empty(&sorted_list)) {
2433 list_add_tail(&device->list, &sorted_list);
2435 struct list_head *pos2, *insert_pos = &sorted_list;
2436 list_for_each(pos2, &sorted_list) {
2437 insert_dev = list_entry(pos2, struct use_case_device, list);
2439 if (device->sort_priority > insert_dev->sort_priority) {
2442 } else if (device->sort_priority == insert_dev->sort_priority) {
2443 if (strcmp(device->name, insert_dev->name) < 0) {
2450 list_add_tail(&device->list, insert_pos);
2454 /* Move sorted list back to verb->device_list */
2455 list_splice_init(&sorted_list, &verb->device_list);
2460 static int verb_device_management(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb)
2462 struct list_head *pos;
2463 struct ucm_dev_name *dev;
2466 /* rename devices */
2467 list_for_each(pos, &verb->rename_list) {
2468 dev = list_entry(pos, struct ucm_dev_name, list);
2469 err = uc_mgr_rename_device(verb, dev->name1, dev->name2);
2471 snd_error(UCM, "cannot rename device '%s' to '%s'", dev->name1, dev->name2);
2476 /* remove devices */
2477 list_for_each(pos, &verb->remove_list) {
2478 dev = list_entry(pos, struct ucm_dev_name, list);
2479 err = uc_mgr_remove_device(verb, dev->name2);
2481 snd_error(UCM, "cannot remove device '%s'", dev->name2);
2486 /* those lists are no longer used */
2487 uc_mgr_free_dev_name_list(&verb->rename_list);
2488 uc_mgr_free_dev_name_list(&verb->remove_list);
2490 /* strip index from single device names */
2491 if (uc_mgr->conf_format >= 8) {
2492 /* sort devices by priority */
2493 err = verb_sort_devices(verb);
2498 /* normalize device names to remove spaces per use-case.h specification */
2499 err = verb_normalize_device_names(uc_mgr, verb);
2503 /* strip index from single device names */
2504 if (uc_mgr->conf_format >= 8) {
2505 err = verb_strip_single_device_index(verb);
2511 /* handle conflicting/supported lists */
2512 return verb_dev_list_check(verb);
2516 * Parse Verb Section
2518 * # Example Use case verb section for Voice call blah
2519 * # By Joe Blogs <joe@blogs.com>
2522 * # enable and disable sequences are compulsory
2524 * cset "name='Master Playback Switch',index=2 0,0"
2525 * cset "name='Master Playback Volume',index=2 25,25"
2527 * cset "name='Master Playback Switch',index=2 1,1"
2528 * cset "name='Master Playback Volume',index=2 50,50"
2532 * cset "name='Master Playback Switch',index=2 0,0"
2533 * cset "name='Master Playback Volume',index=2 25,25"
2535 * cset "name='Master Playback Switch',index=2 1,1"
2536 * cset "name='Master Playback Volume',index=2 50,50"
2539 * # Optional transition verb
2540 * TransitionSequence."ToCaseName" [
2544 * # Optional TQ and ALSA PCMs
2548 * PlaybackPCM "hw:0"
2552 static int parse_verb(snd_use_case_mgr_t *uc_mgr,
2553 struct use_case_verb *verb,
2556 snd_config_iterator_t i, next;
2560 /* in-place evaluation */
2561 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
2565 /* parse verb section */
2566 snd_config_for_each(i, next, cfg) {
2568 n = snd_config_iterator_entry(i);
2569 if (snd_config_get_id(n, &id) < 0)
2572 if (strcmp(id, "EnableSequence") == 0) {
2573 err = parse_sequence(uc_mgr, &verb->enable_list, n);
2575 snd_error(UCM, "failed to parse verb enable sequence");
2581 if (strcmp(id, "DisableSequence") == 0) {
2582 err = parse_sequence(uc_mgr, &verb->disable_list, n);
2584 snd_error(UCM, "failed to parse verb disable sequence");
2590 if (strcmp(id, "TransitionSequence") == 0) {
2591 snd_debug(UCM, "Parse TransitionSequence");
2592 err = parse_transition(uc_mgr, &verb->transition_list, n);
2594 snd_error(UCM, "failed to parse transition sequence");
2600 if (strcmp(id, "Value") == 0) {
2601 err = parse_value(uc_mgr, &verb->value_list, n);
2612 * Parse a Use case verb configuration.
2614 * This configuration contains the following :-
2615 * o Verb enable and disable sequences.
2616 * o Supported Device enable and disable sequences for verb.
2617 * o Supported Modifier enable and disable sequences for verb
2618 * o Optional QoS for the verb and modifiers.
2619 * o Optional PCM device ID for verb and modifiers
2620 * o Alias kcontrols IDs for master and volumes and mutes.
2622 static int parse_verb_config(snd_use_case_mgr_t *uc_mgr,
2623 const char *use_case_name,
2624 const char *comment,
2628 snd_config_iterator_t i, next;
2630 struct use_case_verb *verb;
2634 verb = calloc(1, sizeof(struct use_case_verb));
2637 INIT_LIST_HEAD(&verb->enable_list);
2638 INIT_LIST_HEAD(&verb->disable_list);
2639 INIT_LIST_HEAD(&verb->transition_list);
2640 INIT_LIST_HEAD(&verb->device_list);
2641 INIT_LIST_HEAD(&verb->cmpt_device_list);
2642 INIT_LIST_HEAD(&verb->modifier_list);
2643 INIT_LIST_HEAD(&verb->value_list);
2644 INIT_LIST_HEAD(&verb->rename_list);
2645 INIT_LIST_HEAD(&verb->remove_list);
2646 list_add_tail(&verb->list, &uc_mgr->verb_list);
2647 if (use_case_name == NULL)
2649 verb->name = strdup(use_case_name);
2650 if (verb->name == NULL)
2653 if (comment != NULL) {
2654 verb->comment = strdup(comment);
2655 if (verb->comment == NULL)
2659 /* in-place evaluation */
2660 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
2664 /* parse master config sections */
2665 snd_config_for_each(i, next, cfg) {
2667 n = snd_config_iterator_entry(i);
2668 if (snd_config_get_id(n, &id) < 0)
2671 /* find verb section and parse it */
2672 if (strcmp(id, "SectionVerb") == 0) {
2673 err = parse_verb(uc_mgr, verb, n);
2675 snd_error(UCM, "%s failed to parse verb", what);
2681 /* find device sections and parse them */
2682 if (strcmp(id, "SectionDevice") == 0) {
2683 err = parse_compound(uc_mgr, n,
2684 parse_device_name, verb, NULL);
2686 snd_error(UCM, "%s failed to parse device", what);
2692 /* find modifier sections and parse them */
2693 if (strcmp(id, "SectionModifier") == 0) {
2694 err = parse_compound(uc_mgr, n,
2695 parse_modifier_name, verb, NULL);
2697 snd_error(UCM, "%s failed to parse modifier", what);
2703 /* device renames */
2704 if (strcmp(id, "RenameDevice") == 0) {
2705 err = parse_dev_name_list(uc_mgr, n, &verb->rename_list);
2707 snd_error(UCM, "%s failed to parse device rename", what);
2714 if (strcmp(id, "RemoveDevice") == 0) {
2715 err = parse_dev_name_list(uc_mgr, n, &verb->remove_list);
2717 snd_error(UCM, "%s failed to parse device remove", what);
2723 /* alsa-lib configuration */
2724 if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) {
2725 err = parse_libconfig(uc_mgr, n);
2727 snd_error(UCM, "%s failed to parse LibConfig", what);
2734 /* use case verb must have at least 1 device */
2735 if (list_empty(&verb->device_list)) {
2736 snd_error(UCM, "no use case device defined");
2740 /* do device rename and delete */
2741 err = verb_device_management(uc_mgr, verb);
2743 snd_error(UCM, "device management error in verb '%s'", verb->name);
2751 * Parse variant information
2753 static int parse_variant(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
2754 char **_vfile, char **_vcomment)
2756 snd_config_iterator_t i, next;
2758 char *file = NULL, *comment = NULL;
2761 /* parse master config sections */
2762 snd_config_for_each(i, next, cfg) {
2764 n = snd_config_iterator_entry(i);
2765 if (snd_config_get_id(n, &id) < 0)
2768 /* get use case verb file name */
2769 if (strcmp(id, "File") == 0) {
2771 err = parse_string_substitute3(uc_mgr, n, &file);
2773 snd_error(UCM, "failed to get File");
2780 /* get optional use case comment */
2781 if (strncmp(id, "Comment", 7) == 0) {
2783 err = parse_string_substitute3(uc_mgr, n, &comment);
2785 snd_error(UCM, "failed to get Comment");
2792 snd_error(UCM, "unknown field '%s' in Variant section", id);
2800 *_vcomment = comment;
2810 * Parse master section for "Use Case" and "File" tags.
2812 static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
2813 void *data1 ATTRIBUTE_UNUSED,
2814 void *data2 ATTRIBUTE_UNUSED)
2816 snd_config_iterator_t i, next;
2817 snd_config_t *n, *variant = NULL, *config = NULL;
2818 char *use_case_name, *file = NULL, *comment = NULL;
2819 bool variant_ok = false;
2822 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
2823 snd_error(UCM, "compound type expected for use case section");
2827 err = parse_get_safe_name(uc_mgr, cfg, NULL, &use_case_name);
2829 snd_error(UCM, "unable to get name for use case section");
2833 /* in-place evaluation */
2834 uc_mgr->parse_master_section = 1;
2835 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
2836 uc_mgr->parse_master_section = 0;
2840 /* parse master config sections */
2841 snd_config_for_each(i, next, cfg) {
2843 n = snd_config_iterator_entry(i);
2844 if (snd_config_get_id(n, &id) < 0)
2847 /* get use case verb file name */
2848 if (strcmp(id, "File") == 0) {
2849 err = parse_string_substitute3(uc_mgr, n, &file);
2851 snd_error(UCM, "failed to get File");
2857 /* get use case verb configuration block (syntax version 8+) */
2858 if (strcmp(id, "Config") == 0) {
2859 if (uc_mgr->conf_format < 8) {
2860 snd_error(UCM, "Config is supported in v8+ syntax");
2864 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
2865 snd_error(UCM, "compound type expected for Config");
2873 /* get optional use case comment */
2874 if (strncmp(id, "Comment", 7) == 0) {
2875 err = parse_string_substitute3(uc_mgr, n, &comment);
2877 snd_error(UCM, "failed to get Comment");
2883 if (uc_mgr->conf_format >= 6 && strcmp(id, "Variant") == 0) {
2884 snd_config_iterator_t i2, next2;
2886 snd_config_for_each(i2, next2, n) {
2889 n2 = snd_config_iterator_entry(i2);
2890 if (snd_config_get_id(n2, &id2) < 0)
2892 err = uc_mgr_evaluate_inplace(uc_mgr, n2);
2895 if (strcmp(use_case_name, id2) == 0)
2901 snd_error(UCM, "unknown field '%s' in SectionUseCase", id);
2904 if (variant && !variant_ok) {
2905 snd_error(UCM, "undefined variant '%s'", use_case_name);
2910 /* check mutual exclusivity of File and Config */
2911 if (file && config) {
2912 snd_error(UCM, "both File and Config specified in SectionUseCase");
2918 snd_debug(UCM, "use_case_name %s file '%s'", use_case_name, file);
2920 /* do we have both use case name and (file or config) ? */
2921 if (!file && !config) {
2922 snd_error(UCM, "use case missing file or config");
2927 /* parse verb from file or config */
2930 /* load config from file */
2931 err = uc_mgr_config_load_file(uc_mgr, file, &cfg);
2934 /* parse the config */
2935 err = parse_verb_config(uc_mgr, use_case_name, comment, cfg, file);
2936 snd_config_delete(cfg);
2938 /* inline config - parse directly */
2939 err = parse_verb_config(uc_mgr, use_case_name, comment, config,
2940 comment ? comment : use_case_name);
2943 /* parse variants */
2944 struct list_head orig_variable_list;
2945 snd_config_t *orig_macros = NULL;
2946 int first_iteration = 1;
2948 /* save original variable list */
2949 err = uc_mgr_duplicate_variables(&orig_variable_list, &uc_mgr->variable_list);
2953 /* save original macros */
2954 if (uc_mgr->macros) {
2955 err = snd_config_copy(&orig_macros, uc_mgr->macros);
2957 goto __variant_error;
2960 snd_config_for_each(i, next, variant) {
2961 char *vfile, *vcomment;
2964 /* restore variables and macros for second and later iterations */
2965 if (!first_iteration) {
2966 uc_mgr_free_value(&uc_mgr->variable_list);
2968 err = uc_mgr_duplicate_variables(&uc_mgr->variable_list, &orig_variable_list);
2970 goto __variant_error;
2972 if (uc_mgr->macros) {
2973 snd_config_delete(uc_mgr->macros);
2974 uc_mgr->macros = NULL;
2977 err = snd_config_copy(&uc_mgr->macros, orig_macros);
2979 goto __variant_error;
2982 first_iteration = 0;
2984 n = snd_config_iterator_entry(i);
2985 if (snd_config_get_id(n, &id) < 0)
2987 if (!parse_is_name_safe(id)) {
2989 goto __variant_error;
2991 err = parse_variant(uc_mgr, n, &vfile, &vcomment);
2994 uc_mgr->parse_variant = id;
2995 if (vfile || file) {
2997 const char *fname = vfile ? vfile : file;
2998 /* load config from file */
2999 err = uc_mgr_config_load_file(uc_mgr, fname, &cfg);
3001 err = parse_verb_config(uc_mgr, id,
3002 vcomment ? vcomment : comment,
3004 snd_config_delete(cfg);
3007 /* inline config from variant */
3008 err = parse_verb_config(uc_mgr, id,
3009 vcomment ? vcomment : comment,
3011 vcomment ? vcomment : (comment ? comment : id));
3013 uc_mgr->parse_variant = NULL;
3021 uc_mgr_free_value(&orig_variable_list);
3023 snd_config_delete(orig_macros);
3027 free(use_case_name);
3034 * parse controls which should be run only at initial boot (forcefully)
3036 static int parse_controls_fixedboot(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
3040 if (!list_empty(&uc_mgr->fixedboot_list)) {
3041 snd_error(UCM, "FixedBoot list is not empty");
3044 err = parse_sequence(uc_mgr, &uc_mgr->fixedboot_list, cfg);
3046 snd_error(UCM, "Unable to parse FixedBootSequence");
3054 * parse controls which should be run only at initial boot
3056 static int parse_controls_boot(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
3060 if (!list_empty(&uc_mgr->boot_list)) {
3061 snd_error(UCM, "Boot list is not empty");
3064 err = parse_sequence(uc_mgr, &uc_mgr->boot_list, cfg);
3066 snd_error(UCM, "Unable to parse BootSequence");
3076 static int parse_controls(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
3080 if (!list_empty(&uc_mgr->default_list)) {
3081 snd_error(UCM, "Default list is not empty");
3084 err = parse_sequence(uc_mgr, &uc_mgr->default_list, cfg);
3086 snd_error(UCM, "Unable to parse SectionDefaults");
3094 * Each sound card has a master sound card file that lists all the supported
3095 * use case verbs for that sound card. i.e.
3097 * #Example master file for blah sound card
3098 * #By Joe Blogs <joe@bloggs.org>
3100 * Comment "Nice Abstracted Soundcard"
3102 * # The file is divided into Use case sections. One section per use case verb.
3104 * SectionUseCase."Voice Call" {
3105 * File "voice_call_blah"
3106 * Comment "Make a voice phone call."
3109 * SectionUseCase."HiFi" {
3111 * Comment "Play and record HiFi quality Music."
3114 * # Define Value defaults
3117 * PlaybackCTL "hw:CARD=0"
3118 * CaptureCTL "hw:CARD=0"
3121 * # The initial boot (run once) configuration.
3124 * cset "name='Master Playback Switch',index=2 1,1"
3125 * cset "name='Master Playback Volume',index=2 25,25"
3128 * # This file also stores the default sound card state.
3131 * cset "name='Master Mono Playback',index=1 0"
3132 * cset "name='Master Mono Playback Volume',index=1 0"
3133 * cset "name='PCM Switch',index=2 1,1"
3134 * exec "some binary here"
3139 * # End of example file.
3141 static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
3143 snd_config_iterator_t i, next;
3148 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
3149 snd_error(UCM, "compound type expected for master file");
3153 if (uc_mgr->conf_format >= 2) {
3154 err = parse_syntax_field(uc_mgr, cfg, uc_mgr->conf_file_name);
3159 /* in-place evaluation */
3160 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
3164 /* parse ValueGlobals first */
3165 err = snd_config_search(cfg, "ValueGlobals", &n);
3167 err = parse_value(uc_mgr, &uc_mgr->global_value_list, n);
3169 snd_error(UCM, "failed to parse ValueGlobals");
3174 err = uc_mgr_check_value(&uc_mgr->global_value_list, "BootCardGroup");
3176 uc_mgr->card_group = true;
3177 /* if we are in boot, skip the main parsing loop */
3178 if (uc_mgr->in_boot)
3182 /* parse master config sections */
3183 snd_config_for_each(i, next, cfg) {
3185 n = snd_config_iterator_entry(i);
3186 if (snd_config_get_id(n, &id) < 0)
3189 if (strcmp(id, "Comment") == 0) {
3190 err = parse_string_substitute3(uc_mgr, n, &uc_mgr->comment);
3192 snd_error(UCM, "failed to get master comment");
3198 /* find use case section and parse it */
3199 if (strcmp(id, "SectionUseCase") == 0) {
3200 err = parse_compound(uc_mgr, n,
3201 parse_master_section,
3208 /* find default control values section (force boot sequence only) */
3209 if (strcmp(id, "FixedBootSequence") == 0) {
3210 err = parse_controls_fixedboot(uc_mgr, n);
3216 /* find default control values section (first boot only) */
3217 if (strcmp(id, "BootSequence") == 0) {
3218 err = parse_controls_boot(uc_mgr, n);
3224 /* find default control values section and parse it */
3225 if (strcmp(id, "SectionDefaults") == 0) {
3226 err = parse_controls(uc_mgr, n);
3232 /* ValueDefaults is now parsed at the top of this function */
3233 if (strcmp(id, "ValueDefaults") == 0) {
3234 err = parse_value(uc_mgr, &uc_mgr->value_list, n);
3236 snd_error(UCM, "failed to parse ValueDefaults");
3242 /* ValueGlobals is parsed at the top of this function */
3243 if (strcmp(id, "ValueGlobals") == 0)
3246 /* alsa-lib configuration */
3247 if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) {
3248 err = parse_libconfig(uc_mgr, n);
3250 snd_error(UCM, "failed to parse LibraryConfig");
3257 if (strcmp(id, "Error") == 0)
3258 return error_node(uc_mgr, n);
3260 /* skip further Syntax value updates (Include) */
3261 if (strcmp(id, "Syntax") == 0)
3264 snd_error(UCM, "unknown master file field %s", id);
3269 /* get the card info */
3270 static int get_card_info(snd_use_case_mgr_t *mgr,
3271 const char *ctl_name,
3272 snd_ctl_card_info_t **info)
3274 struct ctl_list *ctl_list;
3277 err = uc_mgr_open_ctl(mgr, &ctl_list, ctl_name, 0);
3282 *info = ctl_list->ctl_info;
3286 /* find the card in the local machine */
3287 static int get_by_card_name(snd_use_case_mgr_t *mgr, const char *card_name)
3290 snd_ctl_card_info_t *info;
3291 const char *_driver, *_name, *_long_name;
3293 snd_ctl_card_info_alloca(&info);
3296 if (snd_card_next(&card) < 0 || card < 0) {
3297 snd_error(UCM, "no soundcards found...");
3304 /* clear the list, keep the only one CTL device */
3305 uc_mgr_free_ctl_list(mgr);
3307 sprintf(name, "hw:%d", card);
3308 err = get_card_info(mgr, name, &info);
3311 _driver = snd_ctl_card_info_get_driver(info);
3312 _name = snd_ctl_card_info_get_name(info);
3313 _long_name = snd_ctl_card_info_get_longname(info);
3314 if (!strcmp(card_name, _driver) ||
3315 !strcmp(card_name, _name) ||
3316 !strcmp(card_name, _long_name))
3320 if (snd_card_next(&card) < 0) {
3321 snd_error(UCM, "snd_card_next");
3326 uc_mgr_free_ctl_list(mgr);
3331 /* set the driver name and long name by the card ctl name */
3332 static inline int get_by_card(snd_use_case_mgr_t *mgr, const char *ctl_name)
3334 return get_card_info(mgr, ctl_name, NULL);
3337 static int parse_toplevel_path(snd_use_case_mgr_t *uc_mgr,
3341 snd_config_iterator_t i, next, i2, next2;
3342 snd_config_t *n, *n2;
3344 char *dir = NULL, *file = NULL, fn[PATH_MAX];
3349 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
3350 snd_error(UCM, "compound type expected for UseCasePath node");
3354 /* parse use case path config sections */
3355 snd_config_for_each(i, next, cfg) {
3356 n = snd_config_iterator_entry(i);
3358 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
3359 snd_error(UCM, "compound type expected for UseCasePath.something node");
3363 if (snd_config_get_id(n, &id) < 0)
3368 /* parse use case path config sections */
3369 snd_config_for_each(i2, next2, n) {
3371 n2 = snd_config_iterator_entry(i2);
3372 if (snd_config_get_id(n2, &id) < 0)
3375 if (strcmp(id, "Version") == 0) {
3376 err = parse_integer_substitute(uc_mgr, n2, &version);
3378 snd_error(UCM, "unable to parse UcmDirectory");
3381 if (version < 1 || version > 2) {
3382 snd_error(UCM, "Version must be 1 or 2");
3389 if (strcmp(id, "Directory") == 0) {
3390 err = parse_string_substitute(uc_mgr, n2, &dir);
3392 snd_error(UCM, "unable to parse Directory");
3398 if (strcmp(id, "File") == 0) {
3399 err = parse_string_substitute(uc_mgr, n2, &file);
3401 snd_error(UCM, "unable to parse File");
3407 snd_error(UCM, "unknown UseCasePath field %s", id);
3411 snd_error(UCM, "Directory is not defined in %s!", filename);
3415 snd_error(UCM, "File is not defined in %s!", filename);
3419 ucm_filename(fn, sizeof(fn), version, dir, file);
3420 snd_trace(UCM, "probing configuration file '%s'", fn);
3421 if (access(fn, R_OK) == 0 && lstat64(fn, &st) == 0) {
3422 if (S_ISLNK(st.st_mode)) {
3424 char *link, *dir2, *p;
3426 link = malloc(PATH_MAX);
3429 r = readlink(fn, link, PATH_MAX - 1);
3435 p = strrchr(link, '/');
3438 dir2 = malloc(PATH_MAX);
3443 strncpy(dir2, dir, PATH_MAX - 1);
3444 strncat(dir2, "/", PATH_MAX - 1);
3445 strncat(dir2, link, PATH_MAX - 1);
3446 fn[PATH_MAX - 1] = '\0';
3452 snd_trace(UCM, "using directory '%s' and file '%s'", dir, file);
3453 if (replace_string(&uc_mgr->conf_dir_name, dir) == NULL)
3455 if (replace_string(&uc_mgr->conf_file_name, file) == NULL)
3457 strncpy(filename, fn, PATH_MAX);
3458 filename[PATH_MAX - 1] = '\0';
3459 uc_mgr->conf_format = version;
3487 static int parse_toplevel_config(snd_use_case_mgr_t *uc_mgr,
3491 snd_config_iterator_t i, next;
3496 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
3497 snd_error(UCM, "compound type expected for toplevel file");
3501 err = parse_syntax_field(uc_mgr, cfg, filename);
3505 /* in-place evaluation */
3506 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
3510 /* parse toplevel config sections */
3511 snd_config_for_each(i, next, cfg) {
3513 n = snd_config_iterator_entry(i);
3514 if (snd_config_get_id(n, &id) < 0)
3517 if (strcmp(id, "UseCasePath") == 0) {
3518 err = parse_toplevel_path(uc_mgr, filename, n);
3524 /* alsa-lib configuration */
3525 if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) {
3526 err = parse_libconfig(uc_mgr, n);
3528 snd_error(UCM, "failed to parse LibConfig");
3534 /* skip further Syntax value updates (Include) */
3535 if (strcmp(id, "Syntax") == 0)
3538 snd_error(UCM, "unknown toplevel field %s", id);
3544 static int load_toplevel_config(snd_use_case_mgr_t *uc_mgr,
3547 char filename[PATH_MAX];
3551 ucm_filename(filename, sizeof(filename), 2, NULL, "ucm.conf");
3553 if (access(filename, R_OK) != 0) {
3554 snd_error(UCM, "Unable to find the top-level configuration file '%s'.", filename);
3558 err = uc_mgr_config_load(2, filename, &tcfg);
3562 /* filename is shared for function input and output! */
3563 err = parse_toplevel_config(uc_mgr, filename, tcfg);
3564 snd_config_delete(tcfg);
3568 err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg);
3570 snd_error(UCM, "could not parse configuration for card %s", uc_mgr->card_name);
3580 /* load master use case file for sound card based on rules in ucm2/ucm.conf
3582 int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
3588 err = snd_config_top(&uc_mgr->local_config);
3592 err = snd_config_top(&uc_mgr->macros);
3596 name = uc_mgr->card_name;
3597 if (strncmp(name, "hw:", 3) == 0) {
3598 err = get_by_card(uc_mgr, name);
3600 snd_error(UCM, "card '%s' is not valid", name);
3603 } else if (strncmp(name, "strict:", 7)) {
3604 /* do not handle the error here */
3605 /* we can refer the virtual UCM config */
3606 get_by_card_name(uc_mgr, name);
3609 err = load_toplevel_config(uc_mgr, &cfg);
3613 err = parse_master_file(uc_mgr, cfg);
3614 if (uc_mgr->macros) {
3615 snd_config_delete(uc_mgr->macros);
3616 uc_mgr->macros = NULL;
3618 snd_config_delete(cfg);
3620 uc_mgr_free_ctl_list(uc_mgr);
3621 uc_mgr_free_verb(uc_mgr);
3627 uc_mgr_free_ctl_list(uc_mgr);
3628 replace_string(&uc_mgr->conf_dir_name, NULL);
3632 static int filename_filter(const struct dirent64 *dirent)
3636 if (dirent->d_type == DT_DIR) {
3637 if (dirent->d_name[0] == '.') {
3638 if (dirent->d_name[1] == '\0')
3640 if (dirent->d_name[1] == '.' &&
3641 dirent->d_name[2] == '\0')
3649 /* scan all cards and comments
3651 * Cards are defined by machines. Each card/machine installs its UCM
3652 * configuration files in a subdirectory with the same name as the sound
3653 * card under /usr/share/alsa/ucm2. This function will scan all the card
3654 * directories and skip the component directories defined in the array
3657 int uc_mgr_scan_master_configs(const char **_list[])
3659 char filename[PATH_MAX], dfl[PATH_MAX], fn[FILENAME_MAX];
3660 char *env = getenv(ALSA_CONFIG_UCM2_VAR);
3661 snd_use_case_mgr_t *uc_mgr;
3662 const char **list, *d_name;
3664 snd_config_t *cfg, *c;
3665 int i, j, cnt, err, cards;
3668 struct dirent64 **namelist;
3673 err = snd_card_next(&i);
3680 cards += 4; /* plug-and-play */
3683 snprintf(filename, sizeof(filename), "%s/conf.virt.d", env);
3685 snprintf(filename, sizeof(filename), "%s/ucm2/conf.virt.d",
3686 snd_config_topdir());
3688 #if defined(_GNU_SOURCE) && \
3689 !defined(__NetBSD__) && \
3690 !defined(__FreeBSD__) && \
3691 !defined(__OpenBSD__) && \
3692 !defined(__DragonFly__) && \
3693 !defined(__sun) && \
3694 !defined(__ANDROID__) && \
3696 #define SORTFUNC versionsort64
3698 #define SORTFUNC alphasort64
3700 err = scandir64(filename, &namelist, filename_filter, SORTFUNC);
3703 snd_error(UCM, "could not scan directory %s: %s", filename, strerror(-err));
3709 if (strlen(filename) + 8 < sizeof(filename)) {
3710 strcat(filename, "/default");
3711 ss = readlink(filename, dfl, sizeof(dfl)-1);
3714 dfl[sizeof(dfl)-1] = '\0';
3715 if (dfl[0] && dfl[strlen(dfl)-1] == '/')
3716 dfl[strlen(dfl)-1] = '\0';
3723 list = calloc(1, (cards + cnt) * 2 * sizeof(char *));
3730 while (j / 2 < cards) {
3731 err = snd_card_next(&i);
3736 snprintf(fn, sizeof(fn), "-hw:%d", i);
3737 err = snd_use_case_mgr_open(&uc_mgr, fn);
3738 if (err == -ENOENT || err == -ENXIO)
3741 snd_error(UCM, "Unable to open '%s': %s", fn, snd_strerror(err));
3744 err = snd_use_case_get(uc_mgr, "comment", (const char **)&s);
3746 err = snd_card_get_longname(i, &s);
3750 snd_use_case_mgr_close(uc_mgr);
3751 list[j] = strdup(fn + 1);
3752 if (list[j] == NULL) {
3761 for (i = 0; i < cnt; i++) {
3763 d_name = namelist[i]->d_name;
3765 snprintf(fn, sizeof(fn), "%s.conf", d_name);
3766 ucm_filename(filename, sizeof(filename), 2, d_name, fn);
3768 if (eaccess(filename, R_OK))
3770 if (access(filename, R_OK))
3774 err = uc_mgr_config_load(2, filename, &cfg);
3777 err = snd_config_search(cfg, "Syntax", &c);
3779 snd_error(UCM, "Syntax field not found in %s", d_name);
3780 snd_config_delete(cfg);
3783 err = snd_config_get_integer(c, &l);
3785 snd_error(UCM, "Syntax field is invalid in %s", d_name);
3786 snd_config_delete(cfg);
3789 if (l < 2 || l > SYNTAX_VERSION_MAX) {
3790 snd_error(UCM, "Incompatible syntax %d in %s", l, d_name);
3791 snd_config_delete(cfg);
3794 err = snd_config_search(cfg, "Comment", &c);
3796 err = parse_string(c, (char **)&list[j+1]);
3798 snd_config_delete(cfg);
3802 snd_config_delete(cfg);
3803 list[j] = strdup(d_name);
3804 if (list[j] == NULL) {
3808 if (strcmp(dfl, list[j]) == 0) {
3809 /* default to top */
3810 const char *save1 = list[j];
3811 const char *save2 = list[j + 1];
3812 memmove(list + 2, list, j * sizeof(char *));
3821 for (i = 0; i < cnt; i++)
3825 for (i = 0; i < j; i++) {
3826 free((void *)list[i * 2]);
3827 free((void *)list[i * 2 + 1]);