]> git.alsa-project.org Git - alsa-lib.git/blob - src/ucm/parser.c
baa191448cd1eda31bc2a449b3c6f636306e2adb
[alsa-lib.git] / src / ucm / parser.c
1 /*
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.
6  *
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.
11  *
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
15  *
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.
22  *
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>
31  */
32
33 #include "ucm_local.h"
34 #include <sys/stat.h>
35 #include <stdbool.h>
36 #include <dirent.h>
37 #include <ctype.h>
38 #include <limits.h>
39
40 static int filename_filter(const struct dirent64 *dirent);
41
42 static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
43                           struct list_head *base,
44                           snd_config_t *cfg);
45
46 /*
47  * compose the absolute ucm filename
48  */
49 static void ucm_filename(char *fn, size_t fn_len, long version,
50                           const char *dir, const char *file)
51 {
52         const char *env = getenv(version > 1 ? ALSA_CONFIG_UCM2_VAR : ALSA_CONFIG_UCM_VAR);
53
54         if (file[0] == '/')
55                 file++;
56         if (env == NULL)
57                 snprintf(fn, fn_len, "%s/%s/%s%s%s",
58                          snd_config_topdir(), version > 1 ? "ucm2" : "ucm",
59                          dir ?: "", dir ? "/" : "", file);
60         else
61                 snprintf(fn, fn_len, "%s/%s%s%s",
62                          env, dir ?: "", dir ? "/" : "", file);
63 }
64
65 /*
66  *
67  */
68 int uc_mgr_config_load_file(snd_use_case_mgr_t *uc_mgr,
69                              const char *file, snd_config_t **cfg)
70 {
71         char filename[PATH_MAX];
72         int err;
73
74         ucm_filename(filename, sizeof(filename), uc_mgr->conf_format,
75                      file[0] == '/' ? NULL : uc_mgr->conf_dir_name,
76                      file);
77         err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg);
78         if (err < 0) {
79                 snd_error(UCM, "failed to open file %s: %d", filename, err);
80                 return err;
81         }
82         return 0;
83 }
84
85 /*
86  * Replace mallocated string
87  */
88 static char *replace_string(char **dst, const char *value)
89 {
90         free(*dst);
91         *dst = value ? strdup(value) : NULL;
92         return *dst;
93 }
94
95 /*
96  * Parse string
97  */
98 static int parse_string(snd_config_t *n, char **res)
99 {
100         int err;
101
102         err = snd_config_get_string(n, (const char **)res);
103         if (err < 0)
104                 return err;
105         *res = strdup(*res);
106         if (*res == NULL)
107                 return -ENOMEM;
108         return 0;
109 }
110
111 /*
112  * Parse string and substitute
113  */
114 static int parse_string_substitute(snd_use_case_mgr_t *uc_mgr,
115                             snd_config_t *n, char **res)
116 {
117         const char *str;
118         char *s;
119         int err;
120
121         err = snd_config_get_string(n, &str);
122         if (err < 0)
123                 return err;
124         err = uc_mgr_get_substituted_value(uc_mgr, &s, str);
125         if (err >= 0)
126                 *res = s;
127         return err;
128 }
129
130 /*
131  * Parse string and substitute
132  */
133 static int parse_string_substitute3(snd_use_case_mgr_t *uc_mgr,
134                              snd_config_t *n, char **res)
135 {
136         if (uc_mgr->conf_format < 3)
137                 return parse_string(n, res);
138         return parse_string_substitute(uc_mgr, n, res);
139 }
140
141 /*
142  * Parse integer with substitution
143  */
144 static int parse_integer_substitute(snd_use_case_mgr_t *uc_mgr,
145                              snd_config_t *n, long *res)
146 {
147         char *s1, *s2;
148         int err;
149
150         err = snd_config_get_ascii(n, &s1);
151         if (err < 0)
152                 return err;
153         err = uc_mgr_get_substituted_value(uc_mgr, &s2, s1);
154         if (err >= 0)
155                 err = safe_strtol(s2, res);
156         free(s2);
157         free(s1);
158         return err;
159 }
160
161 /*
162  * Parse integer with substitution
163  */
164 static int parse_integer_substitute3(snd_use_case_mgr_t *uc_mgr,
165                               snd_config_t *n, long *res)
166 {
167         char *s1, *s2;
168         int err;
169
170         err = snd_config_get_ascii(n, &s1);
171         if (err < 0)
172                 return err;
173         if (uc_mgr->conf_format < 3)
174                 s2 = s1;
175         else
176                 err = uc_mgr_get_substituted_value(uc_mgr, &s2, s1);
177         if (err >= 0)
178                 err = safe_strtol(s2, res);
179         if (s1 != s2)
180                 free(s2);
181         free(s1);
182         return err;
183 }
184
185 /*
186  * Parse safe ID
187  */
188 static int parse_is_name_safe(const char *name)
189 {
190         if (strchr(name, '.')) {
191                 snd_error(UCM, "char '.' not allowed in '%s'", name);
192                 return 0;
193         }
194         return 1;
195 }
196
197 static int get_string3(snd_use_case_mgr_t *uc_mgr, const char *s1, char **s)
198 {
199         if (uc_mgr->conf_format < 3) {
200                 *s = strdup(s1);
201                 if (*s == NULL)
202                         return -ENOMEM;
203                 return 0;
204         }
205         return uc_mgr_get_substituted_value(uc_mgr, s, s1);
206 }
207
208 static int parse_get_safe_name(snd_use_case_mgr_t *uc_mgr, snd_config_t *n,
209                         const char *alt, char **name)
210 {
211         const char *id;
212         int err;
213
214         if (alt) {
215                 id = alt;
216         } else {
217                 err = snd_config_get_id(n, &id);
218                 if (err < 0)
219                         return err;
220         }
221         err = get_string3(uc_mgr, id, name);
222         if (err < 0)
223                 return err;
224         if (!parse_is_name_safe(*name)) {
225                 free(*name);
226                 return -EINVAL;
227         }
228         return 0;
229 }
230
231 /*
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.
236  */
237 static int parse_device_index(char **name, int *index)
238 {
239         char *p, *num_start;
240         long idx;
241         size_t len;
242         int err;
243
244         if (!name || !*name || !index)
245                 return -EINVAL;
246
247         len = strlen(*name);
248         if (len == 0)
249                 return -EINVAL;
250
251         /* Start from the end and find where digits begin */
252         p = *name + len - 1;
253
254         /* Skip trailing digits */
255         while (p > *name && isdigit(*p))
256                 p--;
257
258         /* If no digits found at the end, index is 0 (no index) */
259         if (p == *name + len - 1) {
260                 *index = 0;
261                 return 0;
262         }
263
264         /* Move to first digit */
265         p++;
266
267         /* Check if there's an optional space before the number */
268         if (p > *name && *(p - 1) == ' ')
269                 p--;
270
271         /* Parse the index */
272         num_start = *p == ' ' ? p + 1 : p;
273         err = safe_strtol(num_start, &idx);
274         if (err < 0) {
275                 /* No valid number found */
276                 *index = 0;
277                 return 0;
278         }
279
280         *index = (int)idx;
281
282         /* Truncate the name to remove the index part */
283         if (*p == ' ')
284                 *p = '\0';  /* Remove space and number */
285         else if (p > *name)
286                 *p = '\0';  /* Remove number only */
287
288         return 0;
289 }
290
291 /*
292  * Handle 'Error' configuration node.
293  */
294 static int error_node(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
295 {
296         int err;
297         char *s;
298
299         err = parse_string_substitute3(uc_mgr, cfg, &s);
300         if (err < 0) {
301                 snd_error(UCM, "failed to get Error string");
302                 return err;
303         }
304         if (!uc_mgr->suppress_nodev_errors)
305                 snd_error(UCM, "%s", s);
306         free(s);
307         return -ENXIO;
308 }
309
310 /*
311  *
312  */
313 static int parse_syntax_field(snd_use_case_mgr_t *uc_mgr,
314                               snd_config_t *cfg, const char *filename)
315 {
316         snd_config_t *n;
317         long l;
318         int err;
319
320         err = snd_config_search(cfg, "Syntax", &n);
321         if (err < 0) {
322                 snd_error(UCM, "Syntax field not found in %s", filename);
323                 return -EINVAL;
324         }
325         err = snd_config_get_integer(n, &l);
326         if (err < 0) {
327                 snd_error(UCM, "Syntax field is invalid in %s", filename);
328                 return err;
329         }
330         if (l < 2 || l > SYNTAX_VERSION_MAX) {
331                 snd_error(UCM, "Incompatible syntax %ld in %s", l, filename);
332                 return -EINVAL;
333         }
334         /* delete this field to optimize strcmp() call in the parsing loop */
335         snd_config_delete(n);
336         uc_mgr->conf_format = l;
337         return l;
338 }
339
340 /*
341  * Evaluate variable regex definitions (in-place delete)
342  */
343 static int evaluate_regex(snd_use_case_mgr_t *uc_mgr,
344                           snd_config_t *cfg)
345 {
346         snd_config_iterator_t i, next;
347         snd_config_t *d, *n;
348         const char *id;
349         int err;
350
351         err = snd_config_search(cfg, "DefineRegex", &d);
352         if (err == -ENOENT)
353                 return 1;
354         if (err < 0)
355                 return err;
356
357         if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
358                 snd_error(UCM, "compound type expected for DefineRegex");
359                 return -EINVAL;
360         }
361
362         if (uc_mgr->conf_format < 3) {
363                 snd_error(UCM, "DefineRegex is supported in v3+ syntax");
364                 return -EINVAL;
365         }
366
367         snd_config_for_each(i, next, d) {
368                 n = snd_config_iterator_entry(i);
369                 err = snd_config_get_id(n, &id);
370                 if (err < 0)
371                         return err;
372                 if (id[0] == '@') {
373                         snd_error(UCM, "value names starting with '@' are reserved for application variables");
374                         return -EINVAL;
375                 }
376                 err = uc_mgr_define_regex(uc_mgr, id, n);
377                 if (err < 0)
378                         return err;
379         }
380
381         snd_config_delete(d);
382         return 0;
383 }
384
385 /*
386  * Evaluate variable definitions (in-place delete)
387  */
388 static int evaluate_define(snd_use_case_mgr_t *uc_mgr,
389                            snd_config_t *cfg)
390 {
391         snd_config_iterator_t i, next;
392         snd_config_t *d, *n;
393         const char *id;
394         char *var, *s;
395         int err;
396
397         err = snd_config_search(cfg, "Define", &d);
398         if (err == -ENOENT)
399                 return evaluate_regex(uc_mgr, cfg);
400         if (err < 0)
401                 return err;
402
403         if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
404                 snd_error(UCM, "compound type expected for Define");
405                 return -EINVAL;
406         }
407
408         if (uc_mgr->conf_format < 3) {
409                 snd_error(UCM, "Define is supported in v3+ syntax");
410                 return -EINVAL;
411         }
412
413         snd_config_for_each(i, next, d) {
414                 n = snd_config_iterator_entry(i);
415                 err = snd_config_get_id(n, &id);
416                 if (err < 0)
417                         return err;
418                 err = snd_config_get_ascii(n, &var);
419                 if (err < 0)
420                         return err;
421                 err = uc_mgr_get_substituted_value(uc_mgr, &s, var);
422                 free(var);
423                 if (err < 0)
424                         return err;
425                 if (id[0] == '@') {
426                         free(s);
427                         snd_error(UCM, "value names starting with '@' are reserved for application variables");
428                         return -EINVAL;
429                 }
430                 err = uc_mgr_set_variable(uc_mgr, id, s);
431                 free(s);
432                 if (err < 0)
433                         return err;
434         }
435
436         snd_config_delete(d);
437
438         return evaluate_regex(uc_mgr, cfg);
439 }
440
441 /*
442  * Evaluate macro definitions (in-place delete)
443  */
444 static int evaluate_define_macro(snd_use_case_mgr_t *uc_mgr,
445                                  snd_config_t *cfg)
446 {
447         snd_config_t *d;
448         int err;
449
450         err = snd_config_search(cfg, "DefineMacro", &d);
451         if (err == -ENOENT)
452                 return 1;
453         if (err < 0)
454                 return err;
455
456         if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
457                 snd_error(UCM, "compound type expected for DefineMacro");
458                 return -EINVAL;
459         }
460
461         if (uc_mgr->conf_format < 6) {
462                 snd_error(UCM, "DefineMacro is supported in v6+ syntax");
463                 return -EINVAL;
464         }
465
466         err = snd_config_merge(uc_mgr->macros, d, 0);
467         if (err < 0)
468                 return err;
469
470         return 0;
471 }
472
473 static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr,
474                            snd_config_t *dst,
475                            snd_config_t *args)
476 {
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;
481         const char *s;
482         int err;
483
484         err = snd_config_get_id(args, &mid);
485         if (err < 0)
486                 return err;
487         err = snd_config_search(uc_mgr->macros, mid, &m);
488         if (err < 0) {
489                 snd_error(UCM, "Macro '%s' is not defined", mid);
490                 return err;
491         }
492
493         a = args;
494         if (snd_config_get_type(args) == SND_CONFIG_TYPE_STRING) {
495                 err = snd_config_get_string(args, &s);
496                 if (err < 0)
497                         return err;
498                 err = snd_config_load_string(&a, s, 0);
499                 if (err < 0)
500                         return err;
501         } else if (snd_config_get_type(args) != SND_CONFIG_TYPE_COMPOUND) {
502                 return -EINVAL;
503         }
504
505         /* set arguments */
506         snd_config_for_each(i, next, a) {
507                 n = snd_config_iterator_entry(i);
508                 err = snd_config_get_id(n, &id);
509                 if (err < 0)
510                         goto __err_path;
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);
514                         goto __err_path;
515                 }
516                 err = snd_config_get_ascii(n, &var);
517                 if (err < 0)
518                         goto __err_path;
519                 if (uc_mgr->conf_format < 7) {
520                         err = uc_mgr_set_variable(uc_mgr, name, var);
521                         free(var);
522                 } else {
523                         err = uc_mgr_get_substituted_value(uc_mgr, &var2, var);
524                         free(var);
525                         if (err >= 0) {
526                                 err = uc_mgr_set_variable(uc_mgr, name, var2);
527                                 free(var2);
528                         }
529                 }
530                 if (err < 0)
531                         goto __err_path;
532         }
533
534         /* merge + substitute variables */
535         err = snd_config_copy(&mc, m);
536         if (err < 0)
537                 goto __err_path;
538         err = uc_mgr_evaluate_inplace(uc_mgr, mc);
539         if (err < 0) {
540                 snd_config_delete(mc);
541                 goto __err_path;
542         }
543         err = uc_mgr_config_tree_merge(uc_mgr, dst, mc, NULL, NULL);
544         snd_config_delete(mc);
545
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);
550                 if (err < 0)
551                         goto __err_path;
552                 snprintf(name, sizeof(name), "__%s", id);
553                 err = uc_mgr_delete_variable(uc_mgr, name);
554                 if (err < 0)
555                         goto __err_path;
556         }
557
558 __err_path:
559         if (a != args)
560                 snd_config_delete(a);
561         return err;
562 }
563
564 /*
565  * Evaluate macro definitions and instances (in-place delete)
566  */
567 static int evaluate_macro(snd_use_case_mgr_t *uc_mgr,
568                           snd_config_t *cfg)
569 {
570         snd_config_iterator_t i, i2, next, next2;
571         snd_config_t *d, *n, *n2;
572         int err, ret;
573
574         ret = evaluate_define_macro(uc_mgr, cfg);
575         if (ret < 0)
576                 return ret;
577
578         err = snd_config_search(cfg, "Macro", &d);
579         if (err == -ENOENT)
580                 return ret;
581         if (err < 0)
582                 return err;
583
584         if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
585                 snd_error(UCM, "compound type expected for DefineMacro");
586                 return -EINVAL;
587         }
588
589         if (uc_mgr->conf_format < 6) {
590                 snd_error(UCM, "Macro is supported in v6+ syntax");
591                 return -EINVAL;
592         }
593
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) {
597                         const char *id;
598                         if (snd_config_get_id(n, &id))
599                                 id = "";
600                         snd_error(UCM, "compound type expected for Macro.%s", id);
601                         return -EINVAL;
602                 }
603                 snd_config_for_each(i2, next2, n) {
604                         n2 = snd_config_iterator_entry(i2);
605                         err = evaluate_macro1(uc_mgr, cfg, n2);
606                         if (err < 0)
607                                 return err;
608                 }
609         }
610
611         snd_config_delete(d);
612
613         return 0;
614 }
615
616 /*
617  * Evaluate include (in-place)
618  */
619 static int evaluate_include(snd_use_case_mgr_t *uc_mgr,
620                             snd_config_t *cfg)
621 {
622         snd_config_t *n;
623         int err;
624
625         err = snd_config_search(cfg, "Include", &n);
626         if (err == -ENOENT)
627                 return 1;
628         if (err < 0)
629                 return err;
630
631         err = uc_mgr_evaluate_include(uc_mgr, cfg, n);
632         snd_config_delete(n);
633         return err;
634 }
635
636 /*
637  * Evaluate condition (in-place)
638  */
639 static int evaluate_condition(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
640 {
641         snd_config_t *n;
642         int err;
643
644         err = snd_config_search(cfg, "If", &n);
645         if (err == -ENOENT)
646                 return 1;
647         if (err < 0)
648                 return err;
649
650         err = uc_mgr_evaluate_condition(uc_mgr, cfg, n);
651         snd_config_delete(n);
652         return err;
653 }
654
655 /*
656  * Evaluate variant (in-place)
657  */
658 static int evaluate_variant(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
659 {
660         snd_config_iterator_t i, next;
661         snd_config_t *n, *c;
662         const char *id;
663         int err;
664
665         err = snd_config_search(cfg, "Variant", &c);
666         if (err == -ENOENT)
667                 return 1;
668         if (err < 0)
669                 return err;
670
671         if (uc_mgr->conf_format < 6) {
672                 snd_error(UCM, "Variant is supported in v6+ syntax");
673                 return -EINVAL;
674         }
675
676         if (uc_mgr->parse_master_section)
677                 return 1;
678
679         if (uc_mgr->parse_variant == NULL)
680                 goto __ret;
681
682         snd_config_for_each(i, next, c) {
683                 n = snd_config_iterator_entry(i);
684
685                 if (snd_config_get_id(n, &id) < 0)
686                         return -EINVAL;
687
688                 if (strcmp(id, uc_mgr->parse_variant))
689                         continue;
690
691                 err = uc_mgr_evaluate_inplace(uc_mgr, n);
692                 if (err < 0)
693                         return err;
694
695                 err = uc_mgr_config_tree_merge(uc_mgr, cfg, n, NULL, NULL);
696                 if (err < 0)
697                         return err;
698                 snd_config_delete(c);
699                 return 0;
700         }
701
702 __ret:
703         snd_config_delete(c);
704         return 1;
705 }
706
707 /*
708  * In-place evaluate
709  */
710 int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
711                             snd_config_t *cfg)
712 {
713         long iterations = 10000;
714         int err1 = 0, err2 = 0, err3 = 0, err4 = 0, err5 = 0;
715
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?)");
719                         return -EINVAL;
720                 }
721                 iterations--;
722                 /* variables at first */
723                 err1 = evaluate_define(uc_mgr, cfg);
724                 if (err1 < 0)
725                         return err1;
726                 /* include at second */
727                 err2 = evaluate_include(uc_mgr, cfg);
728                 if (err2 < 0)
729                         return err2;
730                 /* include or macro may define another variables */
731                 /* conditions may depend on them */
732                 if (err2 == 0)
733                         continue;
734                 err3 = evaluate_variant(uc_mgr, cfg);
735                 if (err3 < 0)
736                         return err3;
737                 if (err3 == 0)
738                         continue;
739                 uc_mgr->macro_hops++;
740                 if (uc_mgr->macro_hops > 100) {
741                         snd_error(UCM, "Maximal macro hops reached!");
742                         return -EINVAL;
743                 }
744                 err4 = evaluate_macro(uc_mgr, cfg);
745                 uc_mgr->macro_hops--;
746                 if (err4 < 0)
747                         return err4;
748                 if (err4 == 0)
749                         continue;
750                 err5 = evaluate_condition(uc_mgr, cfg);
751                 if (err5 < 0)
752                         return err5;
753         }
754         return 0;
755 }
756
757 /*
758  * Parse one item for alsa-lib config
759  */
760 static int parse_libconfig1(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
761 {
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;
766         int err;
767
768         if (snd_config_get_id(cfg, &id) < 0)
769                 return -EINVAL;
770
771         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
772                 snd_error(UCM, "compound type expected for %s", id);
773                 return -EINVAL;
774         }
775
776         snd_config_for_each(i, next, cfg) {
777                 n = snd_config_iterator_entry(i);
778
779                 if (snd_config_get_id(n, &id) < 0)
780                         return -EINVAL;
781
782                 if (strcmp(id, "File") == 0 ||
783                     strcmp(id, "SubstiFile") == 0) {
784                         substfile = id[0] == 'S';
785                         err = snd_config_get_string(n, &file);
786                         if (err < 0)
787                                 return err;
788                         continue;
789                 }
790
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)
795                                 return -EINVAL;
796                         config = n;
797                         continue;
798                 }
799
800                 snd_error(UCM, "unknown field %s", id);
801                 return -EINVAL;
802         }
803
804         if (file) {
805                 if (substfile) {
806                         snd_config_t *cfg;
807                         err = uc_mgr_config_load_file(uc_mgr, file, &cfg);
808                         if (err < 0)
809                                 return err;
810                         err = uc_mgr_substitute_tree(uc_mgr, cfg);
811                         if (err < 0) {
812                                 snd_config_delete(cfg);
813                                 return err;
814                         }
815                         err = snd_config_merge(uc_mgr->local_config, cfg, 0);
816                         if (err < 0) {
817                                 snd_config_delete(cfg);
818                                 return err;
819                         }
820                 } else {
821                         char filename[PATH_MAX];
822
823                         ucm_filename(filename, sizeof(filename), uc_mgr->conf_format,
824                                      file[0] == '/' ? NULL : uc_mgr->conf_dir_name,
825                                      file);
826                         err = uc_mgr_config_load_into(uc_mgr->conf_format, filename, uc_mgr->local_config);
827                         if (err < 0)
828                                 return err;
829                 }
830         }
831
832         if (config) {
833                 if (substconfig) {
834                         err = uc_mgr_substitute_tree(uc_mgr, config);
835                         if (err < 0)
836                                 return err;
837                 }
838                 err = snd_config_merge(uc_mgr->local_config, config, 0);
839                 if (err < 0)
840                         return err;
841         }
842
843         return 0;
844 }
845
846 /*
847  * Parse alsa-lib config
848  */
849 static int parse_libconfig(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
850 {
851         snd_config_iterator_t i, next;
852         snd_config_t *n;
853         const char *id;
854         int err;
855
856         if (snd_config_get_id(cfg, &id) < 0)
857                 return -EINVAL;
858
859         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
860                 snd_error(UCM, "compound type expected for %s", id);
861                 return -EINVAL;
862         }
863
864         snd_config_for_each(i, next, cfg) {
865                 n = snd_config_iterator_entry(i);
866
867                 err = parse_libconfig1(uc_mgr, n);
868                 if (err < 0)
869                         return err;
870         }
871
872         return 0;
873 }
874
875 /*
876  * Parse transition
877  */
878 static int parse_transition(snd_use_case_mgr_t *uc_mgr,
879                             struct list_head *tlist,
880                             snd_config_t *cfg)
881 {
882         struct transition_sequence *tseq;
883         const char *id;
884         snd_config_iterator_t i, next;
885         snd_config_t *n;
886         int err;
887
888         if (snd_config_get_id(cfg, &id) < 0)
889                 return -EINVAL;
890
891         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
892                 snd_error(UCM, "compound type expected for %s", id);
893                 return -EINVAL;
894         }
895
896         snd_config_for_each(i, next, cfg) {
897                 n = snd_config_iterator_entry(i);
898
899                 if (snd_config_get_id(n, &id) < 0)
900                         return -EINVAL;
901
902                 tseq = calloc(1, sizeof(*tseq));
903                 if (tseq == NULL)
904                         return -ENOMEM;
905                 INIT_LIST_HEAD(&tseq->transition_list);
906
907                 err = get_string3(uc_mgr, id, &tseq->name);
908                 if (err < 0) {
909                         free(tseq);
910                         return err;
911                 }
912
913                 err = parse_sequence(uc_mgr, &tseq->transition_list, n);
914                 if (err < 0) {
915                         uc_mgr_free_transition_element(tseq);
916                         return err;
917                 }
918
919                 list_add(&tseq->list, tlist);
920         }
921         return 0;
922 }
923
924 /*
925  * Parse compound
926  */
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)
930 {
931         const char *id;
932         snd_config_iterator_t i, next;
933         snd_config_t *n;
934         int err;
935
936         if (snd_config_get_id(cfg, &id) < 0)
937                 return -EINVAL;
938
939         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
940                 snd_error(UCM, "compound type expected for %s", id);
941                 return -EINVAL;
942         }
943         /* parse compound */
944         snd_config_for_each(i, next, cfg) {
945                 n = snd_config_iterator_entry(i);
946
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));
949                         return -EINVAL;
950                 }
951
952                 err = fcn(uc_mgr, n, data1, data2);
953                 if (err < 0)
954                         return err;
955         }
956
957         return 0;
958 }
959
960 static int strip_legacy_dev_index(char *name)
961 {
962         char *dot = strchr(name, '.');
963         if (!dot)
964                 return 0;
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);
968
969                 return -EINVAL;
970         }
971         *dot = '\0';
972         return 0;
973 }
974
975 /*
976  * Parse device list
977  */
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,
981                              snd_config_t *cfg)
982 {
983         struct dev_list_node *sdev;
984         const char *id;
985         snd_config_iterator_t i, next;
986         snd_config_t *n;
987         int err;
988
989         if (dev_list->type != DEVLIST_NONE) {
990                 snd_error(UCM, "multiple supported or conflicting device lists");
991
992                 return -EEXIST;
993         }
994
995         if (snd_config_get_id(cfg, &id) < 0)
996                 return -EINVAL;
997
998         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
999                 snd_error(UCM, "compound type expected for %s", id);
1000                 return -EINVAL;
1001         }
1002
1003         snd_config_for_each(i, next, cfg) {
1004                 n = snd_config_iterator_entry(i);
1005
1006                 if (snd_config_get_id(n, &id) < 0)
1007                         return -EINVAL;
1008
1009                 sdev = calloc(1, sizeof(struct dev_list_node));
1010                 if (sdev == NULL)
1011                         return -ENOMEM;
1012                 err = parse_string_substitute3(uc_mgr, n, &sdev->name);
1013                 if (err < 0) {
1014                         free(sdev);
1015                         return err;
1016                 }
1017                 err = strip_legacy_dev_index(sdev->name);
1018                 if (err < 0) {
1019                         free(sdev->name);
1020                         free(sdev);
1021                         return err;
1022                 }
1023                 list_add(&sdev->list, &dev_list->list);
1024         }
1025
1026         dev_list->type = type;
1027
1028         return 0;
1029 }
1030
1031 /* Find a component device by its name, and remove it from machine device
1032  * list.
1033  *
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.
1042  */
1043 struct use_case_device *find_component_dev(snd_use_case_mgr_t *uc_mgr,
1044         const char *name)
1045 {
1046         struct list_head *pos, *posdev, *_posdev;
1047         struct use_case_verb *verb;
1048         struct use_case_device *dev;
1049
1050         list_for_each(pos, &uc_mgr->verb_list) {
1051                 verb = list_entry(pos, struct use_case_verb, list);
1052
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))
1057                                 return dev;
1058                 }
1059
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
1066                                  * list.
1067                                  */
1068                                 list_del(&dev->list);
1069                                 list_add_tail(&dev->list,
1070                                               &verb->cmpt_device_list);
1071                                 return dev;
1072                         }
1073                 }
1074         }
1075
1076         return NULL;
1077 }
1078
1079 /* parse sequence of a component device
1080  *
1081  * This function will find the component device and mark if its enable or
1082  * disable sequence is needed by its parenet device.
1083  */
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)
1087 {
1088         char *val;
1089         int err;
1090
1091         err = parse_string_substitute3(uc_mgr, n, &val);
1092         if (err < 0)
1093                 return err;
1094
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);
1098                 free(val);
1099                 return -EINVAL;
1100         }
1101         free(val);
1102
1103         /* Parent needs its enable or disable sequence */
1104         cmpt_seq->enable = enable;
1105
1106         return 0;
1107 }
1108
1109 /*
1110  * Parse sequences.
1111  *
1112  * Sequence controls elements  are in the following form:-
1113  *
1114  * cdev "hw:0"
1115  * cset "element_id_syntax value_syntax"
1116  * usleep time
1117  * exec "any unix command with arguments"
1118  * enadev "component device name"
1119  * disdev "component device name"
1120  *
1121  * e.g.
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"
1126  */
1127 static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
1128                           struct list_head *base,
1129                           snd_config_t *cfg)
1130 {
1131         struct sequence_element *curr;
1132         snd_config_iterator_t i, next;
1133         snd_config_t *n;
1134         int err, idx = 0;
1135         const char *cmd = NULL;
1136
1137         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1138                 snd_error(UCM, "compound is expected for sequence definition");
1139                 return -EINVAL;
1140         }
1141
1142         snd_config_for_each(i, next, cfg) {
1143                 const char *id;
1144                 idx ^= 1;
1145                 n = snd_config_iterator_entry(i);
1146                 err = snd_config_get_id(n, &id);
1147                 if (err < 0)
1148                         continue;
1149                 if (idx == 1) {
1150                         if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
1151                                 snd_error(UCM, "string type is expected for sequence command");
1152                                 return -EINVAL;
1153                         }
1154                         err = snd_config_get_string(n, &cmd);
1155                         if (err < 0)
1156                                 return err;
1157                         continue;
1158                 }
1159
1160                 /* alloc new sequence element */
1161                 curr = calloc(1, sizeof(struct sequence_element));
1162                 if (curr == NULL)
1163                         return -ENOMEM;
1164                 list_add_tail(&curr->list, base);
1165
1166                 if (strcmp(cmd, "cdev") == 0) {
1167                         curr->type = SEQUENCE_ELEMENT_TYPE_CDEV;
1168                         err = parse_string_substitute3(uc_mgr, n, &curr->data.cdev);
1169                         if (err < 0) {
1170                                 snd_error(UCM, "cdev requires a string!");
1171                                 return err;
1172                         }
1173                         continue;
1174                 }
1175
1176                 if (strcmp(cmd, "cset") == 0) {
1177                         curr->type = SEQUENCE_ELEMENT_TYPE_CSET;
1178 cset:
1179                         err = parse_string_substitute3(uc_mgr, n, &curr->data.cset);
1180                         if (err < 0) {
1181                                 snd_error(UCM, "%s requires a string!", cmd);
1182                                 return err;
1183                         }
1184                         continue;
1185                 }
1186
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);
1194                         if (err < 0) {
1195                                 snd_error(UCM, "%s requires a valid device!", cmd);
1196                                 return err;
1197                         }
1198                         continue;
1199                 }
1200
1201                 if (strcmp(cmd, "enadev2") == 0) {
1202                         curr->type = SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ;
1203                         goto device;
1204                 }
1205
1206                 if (strcmp(cmd, "disdev2") == 0) {
1207                         curr->type = SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_SEQ;
1208 device:
1209                         err = parse_string_substitute3(uc_mgr, n, &curr->data.device);
1210                         if (err < 0) {
1211                                 snd_error(UCM, "%s requires a valid device!", cmd);
1212                                 return err;
1213                         }
1214                         continue;
1215                 }
1216
1217                 if (strcmp(cmd, "disdevall") == 0) {
1218                         curr->type = SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_ALL;
1219                         continue;
1220                 }
1221
1222                 if (strcmp(cmd, "cset-bin-file") == 0) {
1223                         curr->type = SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE;
1224                         goto cset;
1225                 }
1226
1227                 if (strcmp(cmd, "cset-tlv") == 0) {
1228                         curr->type = SEQUENCE_ELEMENT_TYPE_CSET_TLV;
1229                         goto cset;
1230                 }
1231
1232                 if (strcmp(cmd, "cset-new") == 0) {
1233                         curr->type = SEQUENCE_ELEMENT_TYPE_CSET_NEW;
1234                         goto cset;
1235                 }
1236
1237                 if (strcmp(cmd, "ctl-remove") == 0) {
1238                         curr->type = SEQUENCE_ELEMENT_TYPE_CTL_REMOVE;
1239                         goto cset;
1240                 }
1241
1242                 if (strcmp(cmd, "sysw") == 0) {
1243                         curr->type = SEQUENCE_ELEMENT_TYPE_SYSSET;
1244                         err = parse_string_substitute3(uc_mgr, n, &curr->data.sysw);
1245                         if (err < 0) {
1246                                 snd_error(UCM, "sysw requires a string!");
1247                                 return err;
1248                         }
1249                         continue;
1250                 }
1251
1252                 if (strcmp(cmd, "usleep") == 0) {
1253                         curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
1254                         err = parse_integer_substitute3(uc_mgr, n, &curr->data.sleep);
1255                         if (err < 0) {
1256                                 snd_error(UCM, "usleep requires integer!");
1257                                 return err;
1258                         }
1259                         continue;
1260                 }
1261
1262                 if (strcmp(cmd, "msleep") == 0) {
1263                         curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
1264                         err = parse_integer_substitute3(uc_mgr, n, &curr->data.sleep);
1265                         if (err < 0) {
1266                                 snd_error(UCM, "msleep requires integer!");
1267                                 return err;
1268                         }
1269                         curr->data.sleep *= 1000L;
1270                         continue;
1271                 }
1272
1273                 if (strcmp(cmd, "exec") == 0) {
1274                         curr->type = SEQUENCE_ELEMENT_TYPE_EXEC;
1275 exec:
1276                         err = parse_string_substitute3(uc_mgr, n, &curr->data.exec);
1277                         if (err < 0) {
1278                                 snd_error(UCM, "exec requires a string!");
1279                                 return err;
1280                         }
1281                         continue;
1282                 }
1283
1284                 if (strcmp(cmd, "shell") == 0) {
1285                         curr->type = SEQUENCE_ELEMENT_TYPE_SHELL;
1286                         goto exec;
1287                 }
1288
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);
1292                         if (err < 0) {
1293                                 snd_error(UCM, "sysw requires a string!");
1294                                 return err;
1295                         }
1296                         continue;
1297                 }
1298
1299                 if (strcmp(cmd, "comment") == 0)
1300                         goto skip;
1301
1302                 snd_error(UCM, "sequence command '%s' is ignored", cmd);
1303
1304 skip:
1305                 list_del(&curr->list);
1306                 uc_mgr_free_sequence_element(curr);
1307         }
1308
1309         return 0;
1310 }
1311
1312 /*
1313  *
1314  */
1315 int uc_mgr_add_value(struct list_head *base, const char *key, char *val)
1316 {
1317         struct ucm_value *curr;
1318
1319         curr = calloc(1, sizeof(struct ucm_value));
1320         if (curr == NULL)
1321                 return -ENOMEM;
1322         curr->name = strdup(key);
1323         if (curr->name == NULL) {
1324                 free(curr);
1325                 return -ENOMEM;
1326         }
1327         list_add_tail(&curr->list, base);
1328         curr->data = val;
1329         return 0;
1330 }
1331
1332 /*
1333  * Parse values.
1334  *
1335  * Parse values describing PCM, control/mixer settings and stream parameters.
1336  *
1337  * Value {
1338  *   TQ Voice
1339  *   CapturePCM "hw:1"
1340  *   PlaybackVolume "name='Master Playback Volume',index=2"
1341  *   PlaybackSwitch "name='Master Playback Switch',index=2"
1342  * }
1343  */
1344 static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
1345                           struct list_head *base,
1346                           snd_config_t *cfg)
1347 {
1348         snd_config_iterator_t i, next;
1349         snd_config_t *n;
1350         char *s;
1351         snd_config_type_t type;
1352         int err;
1353
1354         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1355                 snd_error(UCM, "compound is expected for value definition");
1356                 return -EINVAL;
1357         }
1358
1359         /* in-place evaluation */
1360         err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1361         if (err < 0)
1362                 return err;
1363
1364         snd_config_for_each(i, next, cfg) {
1365                 const char *id;
1366                 n = snd_config_iterator_entry(i);
1367                 err = snd_config_get_id(n, &id);
1368                 if (err < 0)
1369                         continue;
1370
1371                 type = snd_config_get_type(n);
1372                 switch (type) {
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);
1377                         if (err < 0) {
1378                                 snd_error(UCM, "unable to parse value for id '%s': %s!", id, snd_strerror(err));
1379                                 return err;
1380                         }
1381                         break;
1382                 case SND_CONFIG_TYPE_STRING:
1383                         err = parse_string_substitute(uc_mgr, n, &s);
1384                         if (err < 0) {
1385                                 snd_error(UCM, "unable to parse a string for id '%s'!", id);
1386                                 return err;
1387                         }
1388                         break;
1389                 default:
1390                         snd_error(UCM, "invalid type %i in Value compound '%s'", type, id);
1391                         return -EINVAL;
1392                 }
1393                 err = uc_mgr_add_value(base, id, s);
1394                 if (err < 0) {
1395                         free(s);
1396                         return err;
1397                 }
1398         }
1399
1400         return 0;
1401 }
1402
1403 /*
1404  * Parse Modifier Use cases
1405  *
1406  * # Each modifier is described in new section. N modifiers are allowed
1407  * SectionModifier."Capture Voice" {
1408  *
1409  *      Comment "Record voice call"
1410  *
1411  *      SupportedDevice [
1412  *              "x"
1413  *              "y"
1414  *      ]
1415  *
1416  *      ConflictingDevice [
1417  *              "x"
1418  *              "y"
1419  *      ]
1420  *
1421  *      EnableSequence [
1422  *              ....
1423  *      ]
1424  *
1425  *      DisableSequence [
1426  *              ...
1427  *      ]
1428  *
1429  *      TransitionSequence."ToModifierName" [
1430  *              ...
1431  *      ]
1432  *
1433  *      # Optional TQ and ALSA PCMs
1434  *      Value {
1435  *              TQ Voice
1436  *              CapturePCM "hw:1"
1437  *              PlaybackVolume "name='Master Playback Volume',index=2"
1438  *              PlaybackSwitch "name='Master Playback Switch',index=2"
1439  *      }
1440  * }
1441  *
1442  * SupportedDevice and ConflictingDevice cannot be specified together.
1443  * Both are optional.
1444  */
1445 static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
1446                           snd_config_t *cfg,
1447                           void *data1, void *data2)
1448 {
1449         struct use_case_verb *verb = data1;
1450         struct use_case_modifier *modifier;
1451         char *name;
1452         snd_config_iterator_t i, next;
1453         snd_config_t *n;
1454         int err;
1455
1456         if (parse_get_safe_name(uc_mgr, cfg, data2, &name) < 0)
1457                 return -EINVAL;
1458
1459         /* allocate modifier */
1460         modifier = calloc(1, sizeof(*modifier));
1461         if (modifier == NULL) {
1462                 free(name);
1463                 return -ENOMEM;
1464         }
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;
1472
1473         /* in-place evaluation */
1474         err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1475         if (err < 0)
1476                 return err;
1477
1478         snd_config_for_each(i, next, cfg) {
1479                 const char *id;
1480                 n = snd_config_iterator_entry(i);
1481                 if (snd_config_get_id(n, &id) < 0)
1482                         continue;
1483
1484                 if (strcmp(id, "Comment") == 0) {
1485                         err = parse_string_substitute3(uc_mgr, n, &modifier->comment);
1486                         if (err < 0) {
1487                                 snd_error(UCM, "failed to get modifier comment");
1488                                 return err;
1489                         }
1490                         continue;
1491                 }
1492
1493                 if (strcmp(id, "SupportedDevice") == 0) {
1494                         err = parse_device_list(uc_mgr, &modifier->dev_list,
1495                                                 DEVLIST_SUPPORTED, n);
1496                         if (err < 0) {
1497                                 snd_error(UCM, "failed to parse supported device list");
1498                                 return err;
1499                         }
1500                 }
1501
1502                 if (strcmp(id, "ConflictingDevice") == 0) {
1503                         err = parse_device_list(uc_mgr, &modifier->dev_list,
1504                                                 DEVLIST_CONFLICTING, n);
1505                         if (err < 0) {
1506                                 snd_error(UCM, "failed to parse conflicting device list");
1507                                 return err;
1508                         }
1509                 }
1510
1511                 if (strcmp(id, "EnableSequence") == 0) {
1512                         err = parse_sequence(uc_mgr, &modifier->enable_list, n);
1513                         if (err < 0) {
1514                                 snd_error(UCM, "failed to parse modifier enable sequence");
1515                                 return err;
1516                         }
1517                         continue;
1518                 }
1519
1520                 if (strcmp(id, "DisableSequence") == 0) {
1521                         err = parse_sequence(uc_mgr, &modifier->disable_list, n);
1522                         if (err < 0) {
1523                                 snd_error(UCM, "failed to parse modifier disable sequence");
1524                                 return err;
1525                         }
1526                         continue;
1527                 }
1528
1529                 if (strcmp(id, "TransitionSequence") == 0) {
1530                         err = parse_transition(uc_mgr, &modifier->transition_list, n);
1531                         if (err < 0) {
1532                                 snd_error(UCM, "failed to parse transition modifier");
1533                                 return err;
1534                         }
1535                         continue;
1536                 }
1537
1538                 if (strcmp(id, "Value") == 0) {
1539                         err = parse_value(uc_mgr, &modifier->value_list, n);
1540                         if (err < 0) {
1541                                 snd_error(UCM, "failed to parse Value");
1542                                 return err;
1543                         }
1544                         continue;
1545                 }
1546         }
1547
1548         return 0;
1549 }
1550
1551 /*
1552  * Parse device configuration fields
1553  */
1554 static int parse_device_fields(snd_use_case_mgr_t *uc_mgr,
1555                                snd_config_t *cfg,
1556                                struct use_case_device *device)
1557 {
1558         snd_config_iterator_t i, next;
1559         snd_config_t *n;
1560         int err;
1561
1562         snd_config_for_each(i, next, cfg) {
1563                 const char *id;
1564                 n = snd_config_iterator_entry(i);
1565                 if (snd_config_get_id(n, &id) < 0)
1566                         continue;
1567
1568                 if (strcmp(id, "Comment") == 0) {
1569                         err = parse_string_substitute3(uc_mgr, n, &device->comment);
1570                         if (err < 0) {
1571                                 snd_error(UCM, "failed to get device comment");
1572                                 return err;
1573                         }
1574                         continue;
1575                 }
1576
1577                 if (strcmp(id, "SupportedDevice") == 0) {
1578                         err = parse_device_list(uc_mgr, &device->dev_list,
1579                                                 DEVLIST_SUPPORTED, n);
1580                         if (err < 0) {
1581                                 snd_error(UCM, "failed to parse supported device list");
1582
1583                                 return err;
1584                         }
1585                 }
1586
1587                 if (strcmp(id, "ConflictingDevice") == 0) {
1588                         err = parse_device_list(uc_mgr, &device->dev_list,
1589                                                 DEVLIST_CONFLICTING, n);
1590                         if (err < 0) {
1591                                 snd_error(UCM, "failed to parse conflicting device list");
1592
1593                                 return err;
1594                         }
1595                 }
1596
1597                 if (strcmp(id, "EnableSequence") == 0) {
1598                         err = parse_sequence(uc_mgr, &device->enable_list, n);
1599                         if (err < 0) {
1600                                 snd_error(UCM, "failed to parse device enable sequence");
1601
1602                                 return err;
1603                         }
1604                         continue;
1605                 }
1606
1607                 if (strcmp(id, "DisableSequence") == 0) {
1608                         err = parse_sequence(uc_mgr, &device->disable_list, n);
1609                         if (err < 0) {
1610                                 snd_error(UCM, "failed to parse device disable sequence");
1611
1612                                 return err;
1613                         }
1614                         continue;
1615                 }
1616
1617                 if (strcmp(id, "TransitionSequence") == 0) {
1618                         err = parse_transition(uc_mgr, &device->transition_list, n);
1619                         if (err < 0) {
1620                                 snd_error(UCM, "failed to parse transition device");
1621
1622                                 return err;
1623                         }
1624                         continue;
1625                 }
1626
1627                 if (strcmp(id, "Value") == 0) {
1628                         err = parse_value(uc_mgr, &device->value_list, n);
1629                         if (err < 0) {
1630                                 snd_error(UCM, "failed to parse Value");
1631                                 return err;
1632                         }
1633                         continue;
1634                 }
1635         }
1636         return 0;
1637 }
1638
1639 /*
1640  * Helper function to copy, evaluate and optionally merge configuration trees.
1641  */
1642 static int uc_mgr_config_copy_eval_merge(snd_use_case_mgr_t *uc_mgr,
1643                                          snd_config_t **dst,
1644                                          snd_config_t *src,
1645                                          snd_config_t *merge_from)
1646 {
1647         snd_config_t *tmp = NULL;
1648         int err;
1649
1650         err = snd_config_copy(&tmp, src);
1651         if (err < 0)
1652                 return err;
1653
1654         err = uc_mgr_evaluate_inplace(uc_mgr, tmp);
1655         if (err < 0) {
1656                 snd_config_delete(tmp);
1657                 return err;
1658         }
1659
1660         if (merge_from) {
1661                 err = uc_mgr_config_tree_merge(uc_mgr, tmp, merge_from, NULL, NULL);
1662                 if (err < 0) {
1663                         snd_config_delete(tmp);
1664                         return err;
1665                 }
1666         }
1667
1668         *dst = tmp;
1669         return 0;
1670 }
1671
1672 /*
1673  * Parse Device Use Cases
1674  *
1675  * # Each device is described in new section. N devices are allowed
1676  * SectionDevice."Headphones" {
1677  *      Comment "Headphones connected to 3.5mm jack"
1678  *
1679  *      SupportedDevice [
1680  *              "x"
1681  *              "y"
1682  *      ]
1683  *
1684  *      ConflictingDevice [
1685  *              "x"
1686  *              "y"
1687  *      ]
1688  *
1689  *      EnableSequence [
1690  *              ....
1691  *      ]
1692  *
1693  *      DisableSequence [
1694  *              ...
1695  *      ]
1696  *
1697  *      TransitionSequence."ToDevice" [
1698  *              ...
1699  *      ]
1700  *
1701  *      Value {
1702  *              PlaybackVolume "name='Master Playback Volume',index=2"
1703  *              PlaybackSwitch "name='Master Playback Switch',index=2"
1704  *      }
1705  * }
1706  */
1707
1708 static int parse_device_by_name(snd_use_case_mgr_t *uc_mgr,
1709                                 snd_config_t *cfg,
1710                                 struct use_case_verb *verb,
1711                                 const char *name,
1712                                 struct use_case_device **ret_device)
1713 {
1714         struct use_case_device *device;
1715         int err;
1716
1717         device = calloc(1, sizeof(*device));
1718         if (device == NULL)
1719                 return -ENOMEM;
1720
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) {
1731                 free(device);
1732                 return -ENOMEM;
1733         }
1734         device->orig_name = strdup(name);
1735         if (device->orig_name == NULL)
1736                 return -ENOMEM;
1737
1738         err = parse_device_fields(uc_mgr, cfg, device);
1739         if (err < 0)
1740                 return err;
1741
1742         if (ret_device)
1743                 *ret_device = device;
1744
1745         return 0;
1746 }
1747
1748 static int parse_device(snd_use_case_mgr_t *uc_mgr,
1749                         snd_config_t *cfg,
1750                         void *data1, void *data2)
1751 {
1752         struct use_case_verb *verb = data1;
1753         char *name, *colon;
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;
1760         snd_config_t *n;
1761         int err;
1762
1763         if (parse_get_safe_name(uc_mgr, cfg, data2, &name) < 0)
1764                 return -EINVAL;
1765
1766         if (uc_mgr->conf_format >= 8 && (colon = strchr(name, ':'))) {
1767                 variant_label = colon + 1;
1768
1769                 err = snd_config_search(cfg, "DeviceVariant", &device_variant);
1770                 if (err == 0) {
1771                         snd_config_t *variant_cfg = NULL;
1772
1773                         /* Save a copy of the primary config for creating variant devices */
1774                         err = snd_config_copy(&primary_cfg_copy, cfg);
1775                         if (err < 0) {
1776                                 free(name);
1777                                 return err;
1778                         }
1779
1780                         err = snd_config_search(device_variant, variant_label, &variant_cfg);
1781                         if (err == 0) {
1782                                 err = uc_mgr_config_copy_eval_merge(uc_mgr, &merged_cfg, cfg, variant_cfg);
1783                                 if (err < 0) {
1784                                         free(name);
1785                                         return err;
1786                                 }
1787                                 cfg = merged_cfg;
1788                         }
1789                 }
1790         }
1791
1792         /* in-place evaluation */
1793         if (cfg != merged_cfg) {
1794                 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1795                 if (err < 0) {
1796                         free(name);
1797                         goto __error;
1798                 }
1799         }
1800
1801         err = parse_device_by_name(uc_mgr, cfg, verb, name, &device);
1802         free(name);
1803         if (err < 0)
1804                 goto __error;
1805
1806         if (merged_cfg) {
1807                 snd_config_delete(merged_cfg);
1808                 merged_cfg = NULL;
1809         }
1810
1811         if (device_variant == NULL)
1812                 goto __end;
1813
1814         if (device->dev_list.type == DEVLIST_SUPPORTED) {
1815                 snd_error(UCM, "DeviceVariant cannot be used with SupportedDevice");
1816                 err = -EINVAL;
1817                 goto __error;
1818         }
1819
1820         if (snd_config_get_type(device_variant) != SND_CONFIG_TYPE_COMPOUND) {
1821                 snd_error(UCM, "compound type expected for DeviceVariant");
1822                 err = -EINVAL;
1823                 goto __error;
1824         }
1825
1826         colon = strchr(device->name, ':');
1827         if (!colon) {
1828                 snd_error(UCM, "DeviceVariant requires ':' in device name");
1829                 err = -EINVAL;
1830                 goto __error;
1831         }
1832
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;
1837
1838                 n = snd_config_iterator_entry(i);
1839
1840                 if (snd_config_get_id(n, &variant_name) < 0)
1841                         continue;
1842
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);
1847
1848                 err = uc_mgr_config_copy_eval_merge(uc_mgr, &merged_cfg, primary_cfg_copy, n);
1849                 if (err < 0)
1850                         goto __error;
1851
1852                 err = parse_device_by_name(uc_mgr, merged_cfg, verb,
1853                                            variant_device_name, &variant);
1854                 snd_config_delete(merged_cfg);
1855                 merged_cfg = NULL;
1856                 if (err < 0)
1857                         goto __error;
1858
1859                 /* Link variant to primary device */
1860                 list_add(&variant->variant_list, &device->variants);
1861
1862                 err = uc_mgr_put_to_dev_list(&device->dev_list, variant->name);
1863                 if (err < 0)
1864                         goto __error;
1865                 if (device->dev_list.type == DEVLIST_NONE)
1866                         device->dev_list.type = DEVLIST_CONFLICTING;
1867         }
1868
1869 __end:
1870         err = 0;
1871 __error:
1872         if (merged_cfg)
1873                 snd_config_delete(merged_cfg);
1874         if (primary_cfg_copy)
1875                 snd_config_delete(primary_cfg_copy);
1876         return err;
1877 }
1878
1879 /*
1880  * Parse Device Rename/Delete Command
1881  *
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"
1887  */
1888 static int parse_dev_name_list(snd_use_case_mgr_t *uc_mgr,
1889                                snd_config_t *cfg,
1890                                struct list_head *list)
1891 {
1892         snd_config_t *n;
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;
1898         int err;
1899
1900         if (snd_config_get_id(cfg, &id) < 0)
1901                 return -EINVAL;
1902
1903         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1904                 snd_error(UCM, "compound type expected for %s", id);
1905                 return -EINVAL;
1906         }
1907
1908         snd_config_for_each(i, next, cfg) {
1909                 n = snd_config_iterator_entry(i);
1910
1911                 if (snd_config_get_id(n, &name1) < 0)
1912                         return -EINVAL;
1913
1914                 err = get_string3(uc_mgr, name1, &name1s);
1915                 if (err < 0)
1916                         return err;
1917
1918                 err = parse_string_substitute3(uc_mgr, n, &name2);
1919                 if (err < 0) {
1920                         free(name1s);
1921                         snd_error(UCM, "failed to get target device name for '%s'", name1);
1922                         return err;
1923                 }
1924
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) {
1929                                 free(name2);
1930                                 free(name1s);
1931                                 return 0;
1932                         }
1933                 }
1934
1935                 free(name1s);
1936
1937                 dev = calloc(1, sizeof(*dev));
1938                 if (dev == NULL) {
1939                         free(name2);
1940                         return -ENOMEM;
1941                 }
1942                 dev->name1 = strdup(name1);
1943                 if (dev->name1 == NULL) {
1944                         free(dev);
1945                         free(name2);
1946                         return -ENOMEM;
1947                 }
1948                 dev->name2 = name2;
1949                 list_add_tail(&dev->list, list);
1950         }
1951
1952         return 0;
1953 }
1954
1955 static int parse_compound_check_legacy(snd_use_case_mgr_t *uc_mgr,
1956           snd_config_t *cfg,
1957           int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
1958           void *data1)
1959 {
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;
1964         int err;
1965
1966         err = snd_config_get_id(cfg, &id);
1967         if (err < 0)
1968                 return err;
1969
1970         snd_config_for_each(i, next, cfg) {
1971                 child_ctr++;
1972                 if (child_ctr > 1) {
1973                         break;
1974                 }
1975
1976                 child = snd_config_iterator_entry(i);
1977
1978                 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1979                         legacy_format = 0;
1980                         break;
1981                 }
1982
1983                 if (snd_config_get_id(child, &idchild) < 0)
1984                         return -EINVAL;
1985
1986                 if (strcmp(idchild, "0")) {
1987                         legacy_format = 0;
1988                         break;
1989                 }
1990         }
1991         if (child_ctr != 1) {
1992                 legacy_format = 0;
1993         }
1994
1995         if (legacy_format)
1996                 return parse_compound(uc_mgr, cfg, fcn, data1, (void *)id);
1997         else
1998                 return fcn(uc_mgr, cfg, data1, NULL);
1999 }
2000
2001 static int parse_device_name(snd_use_case_mgr_t *uc_mgr,
2002                              snd_config_t *cfg,
2003                              void *data1,
2004                              void *data2 ATTRIBUTE_UNUSED)
2005 {
2006         return parse_compound_check_legacy(uc_mgr, cfg, parse_device, data1);
2007 }
2008
2009 static int parse_modifier_name(snd_use_case_mgr_t *uc_mgr,
2010                              snd_config_t *cfg,
2011                              void *data1,
2012                              void *data2 ATTRIBUTE_UNUSED)
2013 {
2014         return parse_compound_check_legacy(uc_mgr, cfg, parse_modifier, data1);
2015 }
2016
2017 static int verb_dev_list_add(struct use_case_verb *verb,
2018                              enum dev_list_type dst_type,
2019                              const char *dst,
2020                              const char *src)
2021 {
2022         struct use_case_device *device;
2023         struct list_head *pos;
2024
2025         list_for_each(pos, &verb->device_list) {
2026                 device = list_entry(pos, struct use_case_device, list);
2027
2028                 if (strcmp(device->name, dst) != 0)
2029                         continue;
2030                 if (device->dev_list.type != dst_type) {
2031                         if (list_empty(&device->dev_list.list)) {
2032                                 device->dev_list.type = dst_type;
2033                         } else {
2034                                 snd_error(UCM, "incompatible device list type ('%s', '%s')", device->orig_name, src);
2035                                 return -EINVAL;
2036                         }
2037                 }
2038                 return uc_mgr_put_to_dev_list(&device->dev_list, src);
2039         }
2040         snd_error(UCM, "unable to find device '%s'", dst);
2041         return -ENOENT;
2042 }
2043
2044 static int verb_dev_list_check(struct use_case_verb *verb)
2045 {
2046         struct list_head *pos, *pos2, *pos3;
2047         struct use_case_device *device, *target_dev;
2048         struct dev_list_node *dlist, *dlist2;
2049         int err, iteration;
2050         int max_iterations = 100; /* safety limit to prevent infinite loops */
2051         bool added_something;
2052
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);
2060                         if (err < 0)
2061                                 return err;
2062                 }
2063         }
2064
2065         /* Second pass: complete other relationships for devices in group.
2066          * For n devices, at most n-1 iterations are needed.
2067          */
2068         for (iteration = 0; iteration < max_iterations; iteration++) {
2069                 added_something = false;
2070
2071                 list_for_each(pos, &verb->device_list) {
2072                         device = list_entry(pos, struct use_case_device, list);
2073
2074                         if (device->dev_list.type == DEVLIST_NONE)
2075                                 continue;
2076
2077                         list_for_each(pos2, &device->dev_list.list) {
2078                                 dlist = list_entry(pos2, struct dev_list_node, list);
2079
2080                                 target_dev = NULL;
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;
2086                                                 break;
2087                                         }
2088                                 }
2089
2090                                 if (!target_dev)
2091                                         continue;
2092
2093                                 list_for_each(pos3, &device->dev_list.list) {
2094                                         dlist2 = list_entry(pos3, struct dev_list_node, list);
2095
2096                                         if (strcmp(dlist2->name, target_dev->name) == 0)
2097                                                 continue;
2098
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);
2102                                         if (err < 0)
2103                                                 return err;
2104                                         if (err > 0)
2105                                                 added_something = true;
2106                                 }
2107                         }
2108                 }
2109
2110                 /* If nothing was added in this iteration, we're done */
2111                 if (!added_something)
2112                         break;
2113         }
2114
2115         if (iteration >= max_iterations) {
2116                 snd_error(UCM, "too many device list iterations for verb '%s'", verb->name);
2117                 return -EINVAL;
2118         }
2119
2120         return 0;
2121 }
2122
2123 /*
2124  * Check if a device name is already in use
2125  */
2126 static int is_device_name_used(struct use_case_verb *verb, const char *name, struct use_case_device *current)
2127 {
2128         struct list_head *pos;
2129         struct use_case_device *device;
2130
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)
2134                         return 1;
2135         }
2136         return 0;
2137 }
2138
2139 /*
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.
2143  */
2144 static int verb_update_device_references(struct use_case_verb *verb,
2145                                           const char *old_name,
2146                                           const char *new_name)
2147 {
2148         struct list_head *pos, *pos2;
2149         struct use_case_device *device;
2150         struct use_case_modifier *modifier;
2151         struct dev_list_node *dlist;
2152         char *name_copy;
2153
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)
2161                                         return -ENOMEM;
2162                                 free(dlist->name);
2163                                 dlist->name = name_copy;
2164                         }
2165                 }
2166         }
2167
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)
2175                                         return -ENOMEM;
2176                                 free(dlist->name);
2177                                 dlist->name = name_copy;
2178                         }
2179                 }
2180         }
2181
2182         return 0;
2183 }
2184
2185 /*
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.
2191  */
2192 static int verb_normalize_device_names(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb)
2193 {
2194         struct list_head *pos;
2195         struct use_case_device *device;
2196         char *orig_name, *norm_name, *colon;
2197         char temp[80];
2198         int err, index;
2199
2200         list_for_each(pos, &verb->device_list) {
2201                 device = list_entry(pos, struct use_case_device, list);
2202
2203                 orig_name = strdup(device->name);
2204                 if (orig_name == NULL)
2205                         return -ENOMEM;
2206
2207                 norm_name = strdup(device->name);
2208                 if (norm_name == NULL) {
2209                         err = -ENOMEM;
2210                         goto __error;
2211                 }
2212
2213                 if (uc_mgr->conf_format < 8)
2214                         goto __no_colon;
2215
2216                 colon = strchr(norm_name, ':');
2217                 if (colon) {
2218                         if (colon[1] == '\0' || strchr(colon + 1, ' ')) {
2219                                 snd_error(UCM, "device descriptor cannot be empty or contain spaces '%s'", orig_name);
2220                                 err = -EINVAL;
2221                                 goto __error;
2222                         }
2223                         *colon = '\0';
2224                         index = 1;
2225                         do {
2226                                 snprintf(temp, sizeof(temp), "%s%d", norm_name, index);
2227                                 if (!is_device_name_used(verb, temp, device))
2228                                         break;
2229                                 index++;
2230                         } while (index < 100); /* Safety limit */
2231                         if (index >= 100) {
2232                                 snd_error(UCM, "too many device name conflicts for '%s'", orig_name);
2233                                 err = -EINVAL;
2234                                 goto __error;
2235                         }
2236
2237                 } else {
2238 __no_colon:
2239                         err = parse_device_index(&norm_name, &index);
2240                         if (err < 0) {
2241                                 snd_error(UCM, "cannot parse device name '%s'", orig_name);
2242                                 goto __error;
2243                         }
2244
2245                         if (index <= 0) {
2246                                 free(orig_name);
2247                                 free(norm_name);
2248                                 continue;
2249                         }
2250                         snprintf(temp, sizeof(temp), "%s%d", norm_name, index);
2251                 }
2252
2253                 free(device->name);
2254                 device->name = strdup(temp);
2255                 if (device->name == NULL) {
2256                         err = -ENOMEM;
2257                         goto __error;
2258                 }
2259
2260                 /* Update all references to the old device name */
2261                 err = verb_update_device_references(verb, orig_name, device->name);
2262                 if (err < 0)
2263                         goto __error;
2264
2265                 free(orig_name);
2266                 free(norm_name);
2267         }
2268
2269         return 0;
2270
2271 __error:
2272         free(orig_name);
2273         free(norm_name);
2274         return err;
2275 }
2276
2277 /*
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").
2282  */
2283 static int verb_strip_single_device_index(struct use_case_verb *verb)
2284 {
2285         struct list_head *pos, *pos2;
2286         struct use_case_device *device, *device2;
2287         char *base_name, *test_base;
2288         char *orig_name;
2289         int count, index, test_index, err;
2290
2291         list_for_each(pos, &verb->device_list) {
2292                 device = list_entry(pos, struct use_case_device, list);
2293
2294                 base_name = strdup(device->name);
2295                 if (base_name == NULL)
2296                         return -ENOMEM;
2297
2298                 err = parse_device_index(&base_name, &index);
2299                 if (err < 0) {
2300                         free(base_name);
2301                         continue;
2302                 }
2303
2304                 if (index <= 0) {
2305                         free(base_name);
2306                         continue;
2307                 }
2308
2309                 /* Count how many devices have the same base name */
2310                 count = 0;
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) {
2315                                 free(base_name);
2316                                 return -ENOMEM;
2317                         }
2318
2319                         err = parse_device_index(&test_base, &test_index);
2320                         if (err >= 0 && strcmp(test_base, base_name) == 0)
2321                                 count++;
2322
2323                         free(test_base);
2324                 }
2325
2326                 if (count == 1) {
2327                         orig_name = device->name;
2328                         device->name = base_name;
2329
2330                         err = verb_update_device_references(verb, orig_name, device->name);
2331                         if (err < 0) {
2332                                 device->name = orig_name;
2333                                 free(base_name);
2334                                 return err;
2335                         }
2336
2337                         free(orig_name);
2338                 } else {
2339                         free(base_name);
2340                 }
2341         }
2342
2343         return 0;
2344 }
2345
2346 /*
2347  * Determine priority for a device.
2348  * Priority order:
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)
2353  */
2354 static long verb_device_get_priority(struct use_case_device *device)
2355 {
2356         struct list_head *pos;
2357         struct ucm_value *val;
2358         const char *priority_str = NULL;
2359         long priority = LONG_MIN;
2360         int err;
2361
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;
2366                         break;
2367                 }
2368         }
2369
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;
2375                                 break;
2376                         }
2377                 }
2378         }
2379
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;
2385                                 break;
2386                         }
2387                 }
2388         }
2389
2390         if (priority_str) {
2391                 err = safe_strtol(priority_str, &priority);
2392                 if (err < 0)
2393                         priority = LONG_MIN;
2394         }
2395
2396         return priority;
2397 }
2398
2399 /*
2400  * Sort devices based on priority values.
2401  * Priority order:
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.
2407  */
2408 static int verb_sort_devices(struct use_case_verb *verb)
2409 {
2410         struct list_head sorted_list;
2411         struct list_head *pos, *npos;
2412         struct use_case_device *device, *insert_dev;
2413
2414         INIT_LIST_HEAD(&sorted_list);
2415
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);
2420         }
2421
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);
2425
2426                 /* Remove device from original list */
2427                 list_del(&device->list);
2428
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);
2434                 } else {
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);
2438
2439                                 if (device->sort_priority > insert_dev->sort_priority) {
2440                                         insert_pos = pos2;
2441                                         break;
2442                                 } else if (device->sort_priority == insert_dev->sort_priority) {
2443                                         if (strcmp(device->name, insert_dev->name) < 0) {
2444                                                 insert_pos = pos2;
2445                                                 break;
2446                                         }
2447                                 }
2448                         }
2449
2450                         list_add_tail(&device->list, insert_pos);
2451                 }
2452         }
2453
2454         /* Move sorted list back to verb->device_list */
2455         list_splice_init(&sorted_list, &verb->device_list);
2456
2457         return 0;
2458 }
2459
2460 static int verb_device_management(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb)
2461 {
2462         struct list_head *pos;
2463         struct ucm_dev_name *dev;
2464         int err;
2465
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);
2470                 if (err < 0) {
2471                         snd_error(UCM, "cannot rename device '%s' to '%s'", dev->name1, dev->name2);
2472                         return err;
2473                 }
2474         }
2475
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);
2480                 if (err < 0) {
2481                         snd_error(UCM, "cannot remove device '%s'", dev->name2);
2482                         return err;
2483                 }
2484         }
2485
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);
2489
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);
2494                 if (err < 0)
2495                         return err;
2496         }
2497
2498         /* normalize device names to remove spaces per use-case.h specification */
2499         err = verb_normalize_device_names(uc_mgr, verb);
2500         if (err < 0)
2501                 return err;
2502
2503         /* strip index from single device names */
2504         if (uc_mgr->conf_format >= 8) {
2505                 err = verb_strip_single_device_index(verb);
2506                 if (err < 0)
2507                         return err;
2508
2509         }
2510
2511         /* handle conflicting/supported lists */
2512         return verb_dev_list_check(verb);
2513 }
2514
2515 /*
2516  * Parse Verb Section
2517  *
2518  * # Example Use case verb section for Voice call blah
2519  * # By Joe Blogs <joe@blogs.com>
2520  *
2521  * SectionVerb {
2522  *      # enable and disable sequences are compulsory
2523  *      EnableSequence [
2524  *              cset "name='Master Playback Switch',index=2 0,0"
2525  *              cset "name='Master Playback Volume',index=2 25,25"
2526  *              msleep 50
2527  *              cset "name='Master Playback Switch',index=2 1,1"
2528  *              cset "name='Master Playback Volume',index=2 50,50"
2529  *      ]
2530  *
2531  *      DisableSequence [
2532  *              cset "name='Master Playback Switch',index=2 0,0"
2533  *              cset "name='Master Playback Volume',index=2 25,25"
2534  *              msleep 50
2535  *              cset "name='Master Playback Switch',index=2 1,1"
2536  *              cset "name='Master Playback Volume',index=2 50,50"
2537  *      ]
2538  *
2539  *      # Optional transition verb
2540  *      TransitionSequence."ToCaseName" [
2541  *              msleep 1
2542  *      ]
2543  *
2544  *      # Optional TQ and ALSA PCMs
2545  *      Value {
2546  *              TQ HiFi
2547  *              CapturePCM "hw:0"
2548  *              PlaybackPCM "hw:0"
2549  *      }
2550  * }
2551  */
2552 static int parse_verb(snd_use_case_mgr_t *uc_mgr,
2553                       struct use_case_verb *verb,
2554                       snd_config_t *cfg)
2555 {
2556         snd_config_iterator_t i, next;
2557         snd_config_t *n;
2558         int err;
2559
2560         /* in-place evaluation */
2561         err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
2562         if (err < 0)
2563                 return err;
2564
2565         /* parse verb section */
2566         snd_config_for_each(i, next, cfg) {
2567                 const char *id;
2568                 n = snd_config_iterator_entry(i);
2569                 if (snd_config_get_id(n, &id) < 0)
2570                         continue;
2571
2572                 if (strcmp(id, "EnableSequence") == 0) {
2573                         err = parse_sequence(uc_mgr, &verb->enable_list, n);
2574                         if (err < 0) {
2575                                 snd_error(UCM, "failed to parse verb enable sequence");
2576                                 return err;
2577                         }
2578                         continue;
2579                 }
2580
2581                 if (strcmp(id, "DisableSequence") == 0) {
2582                         err = parse_sequence(uc_mgr, &verb->disable_list, n);
2583                         if (err < 0) {
2584                                 snd_error(UCM, "failed to parse verb disable sequence");
2585                                 return err;
2586                         }
2587                         continue;
2588                 }
2589
2590                 if (strcmp(id, "TransitionSequence") == 0) {
2591                         snd_debug(UCM, "Parse TransitionSequence");
2592                         err = parse_transition(uc_mgr, &verb->transition_list, n);
2593                         if (err < 0) {
2594                                 snd_error(UCM, "failed to parse transition sequence");
2595                                 return err;
2596                         }
2597                         continue;
2598                 }
2599
2600                 if (strcmp(id, "Value") == 0) {
2601                         err = parse_value(uc_mgr, &verb->value_list, n);
2602                         if (err < 0)
2603                                 return err;
2604                         continue;
2605                 }
2606         }
2607
2608         return 0;
2609 }
2610
2611 /*
2612  * Parse a Use case verb configuration.
2613  *
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.
2621  */
2622 static int parse_verb_config(snd_use_case_mgr_t *uc_mgr,
2623                              const char *use_case_name,
2624                              const char *comment,
2625                              snd_config_t *cfg,
2626                              const char *what)
2627 {
2628         snd_config_iterator_t i, next;
2629         snd_config_t *n;
2630         struct use_case_verb *verb;
2631         int err;
2632
2633         /* allocate verb */
2634         verb = calloc(1, sizeof(struct use_case_verb));
2635         if (verb == NULL)
2636                 return -ENOMEM;
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)
2648                 return -EINVAL;
2649         verb->name = strdup(use_case_name);
2650         if (verb->name == NULL)
2651                 return -ENOMEM;
2652
2653         if (comment != NULL) {
2654                 verb->comment = strdup(comment);
2655                 if (verb->comment == NULL)
2656                         return -ENOMEM;
2657         }
2658
2659         /* in-place evaluation */
2660         err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
2661         if (err < 0)
2662                 return err;
2663
2664         /* parse master config sections */
2665         snd_config_for_each(i, next, cfg) {
2666                 const char *id;
2667                 n = snd_config_iterator_entry(i);
2668                 if (snd_config_get_id(n, &id) < 0)
2669                         continue;
2670
2671                 /* find verb section and parse it */
2672                 if (strcmp(id, "SectionVerb") == 0) {
2673                         err = parse_verb(uc_mgr, verb, n);
2674                         if (err < 0) {
2675                                 snd_error(UCM, "%s failed to parse verb", what);
2676                                 return err;
2677                         }
2678                         continue;
2679                 }
2680
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);
2685                         if (err < 0) {
2686                                 snd_error(UCM, "%s failed to parse device", what);
2687                                 return err;
2688                         }
2689                         continue;
2690                 }
2691
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);
2696                         if (err < 0) {
2697                                 snd_error(UCM, "%s failed to parse modifier", what);
2698                                 return err;
2699                         }
2700                         continue;
2701                 }
2702
2703                 /* device renames */
2704                 if (strcmp(id, "RenameDevice") == 0) {
2705                         err = parse_dev_name_list(uc_mgr, n, &verb->rename_list);
2706                         if (err < 0) {
2707                                 snd_error(UCM, "%s failed to parse device rename", what);
2708                                 return err;
2709                         }
2710                         continue;
2711                 }
2712
2713                 /* device remove */
2714                 if (strcmp(id, "RemoveDevice") == 0) {
2715                         err = parse_dev_name_list(uc_mgr, n, &verb->remove_list);
2716                         if (err < 0) {
2717                                 snd_error(UCM, "%s failed to parse device remove", what);
2718                                 return err;
2719                         }
2720                         continue;
2721                 }
2722
2723                 /* alsa-lib configuration */
2724                 if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) {
2725                         err = parse_libconfig(uc_mgr, n);
2726                         if (err < 0) {
2727                                 snd_error(UCM, "%s failed to parse LibConfig", what);
2728                                 return err;
2729                         }
2730                         continue;
2731                 }
2732         }
2733
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");
2737                 return -EINVAL;
2738         }
2739
2740         /* do device rename and delete */
2741         err = verb_device_management(uc_mgr, verb);
2742         if (err < 0) {
2743                 snd_error(UCM, "device management error in verb '%s'", verb->name);
2744                 return err;
2745         }
2746
2747         return 0;
2748 }
2749
2750 /*
2751  * Parse variant information
2752  */
2753 static int parse_variant(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
2754                          char **_vfile, char **_vcomment)
2755 {
2756         snd_config_iterator_t i, next;
2757         snd_config_t *n;
2758         char *file = NULL, *comment = NULL;
2759         int err;
2760
2761         /* parse master config sections */
2762         snd_config_for_each(i, next, cfg) {
2763                 const char *id;
2764                 n = snd_config_iterator_entry(i);
2765                 if (snd_config_get_id(n, &id) < 0)
2766                         continue;
2767
2768                 /* get use case verb file name */
2769                 if (strcmp(id, "File") == 0) {
2770                         if (_vfile) {
2771                                 err = parse_string_substitute3(uc_mgr, n, &file);
2772                                 if (err < 0) {
2773                                         snd_error(UCM, "failed to get File");
2774                                         goto __error;
2775                                 }
2776                         }
2777                         continue;
2778                 }
2779
2780                 /* get optional use case comment */
2781                 if (strncmp(id, "Comment", 7) == 0) {
2782                         if (_vcomment) {
2783                                 err = parse_string_substitute3(uc_mgr, n, &comment);
2784                                 if (err < 0) {
2785                                         snd_error(UCM, "failed to get Comment");
2786                                         goto __error;
2787                                 }
2788                         }
2789                         continue;
2790                 }
2791
2792                 snd_error(UCM, "unknown field '%s' in Variant section", id);
2793                 err = -EINVAL;
2794                 goto __error;
2795         }
2796
2797         if (_vfile)
2798                 *_vfile = file;
2799         if (_vcomment)
2800                 *_vcomment = comment;
2801         return 0;
2802
2803 __error:
2804         free(file);
2805         free(comment);
2806         return err;
2807 }
2808
2809 /*
2810  * Parse master section for "Use Case" and "File" tags.
2811  */
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)
2815 {
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;
2820         int err;
2821
2822         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
2823                 snd_error(UCM, "compound type expected for use case section");
2824                 return -EINVAL;
2825         }
2826
2827         err = parse_get_safe_name(uc_mgr, cfg, NULL, &use_case_name);
2828         if (err < 0) {
2829                 snd_error(UCM, "unable to get name for use case section");
2830                 return err;
2831         }
2832
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;
2837         if (err < 0)
2838                 goto __error;
2839
2840         /* parse master config sections */
2841         snd_config_for_each(i, next, cfg) {
2842                 const char *id;
2843                 n = snd_config_iterator_entry(i);
2844                 if (snd_config_get_id(n, &id) < 0)
2845                         continue;
2846
2847                 /* get use case verb file name */
2848                 if (strcmp(id, "File") == 0) {
2849                         err = parse_string_substitute3(uc_mgr, n, &file);
2850                         if (err < 0) {
2851                                 snd_error(UCM, "failed to get File");
2852                                 goto __error;
2853                         }
2854                         continue;
2855                 }
2856
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");
2861                                 err = -EINVAL;
2862                                 goto __error;
2863                         }
2864                         if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
2865                                 snd_error(UCM, "compound type expected for Config");
2866                                 err = -EINVAL;
2867                                 goto __error;
2868                         }
2869                         config = n;
2870                         continue;
2871                 }
2872
2873                 /* get optional use case comment */
2874                 if (strncmp(id, "Comment", 7) == 0) {
2875                         err = parse_string_substitute3(uc_mgr, n, &comment);
2876                         if (err < 0) {
2877                                 snd_error(UCM, "failed to get Comment");
2878                                 goto __error;
2879                         }
2880                         continue;
2881                 }
2882
2883                 if (uc_mgr->conf_format >= 6 && strcmp(id, "Variant") == 0) {
2884                         snd_config_iterator_t i2, next2;
2885                         variant = n;
2886                         snd_config_for_each(i2, next2, n) {
2887                                 const char *id2;
2888                                 snd_config_t *n2;
2889                                 n2 = snd_config_iterator_entry(i2);
2890                                 if (snd_config_get_id(n2, &id2) < 0)
2891                                         continue;
2892                                 err = uc_mgr_evaluate_inplace(uc_mgr, n2);
2893                                 if (err < 0)
2894                                         goto __error;
2895                                 if (strcmp(use_case_name, id2) == 0)
2896                                         variant_ok = true;
2897                         }
2898                         continue;
2899                 }
2900
2901                 snd_error(UCM, "unknown field '%s' in SectionUseCase", id);
2902         }
2903
2904         if (variant && !variant_ok) {
2905                 snd_error(UCM, "undefined variant '%s'", use_case_name);
2906                 err = -EINVAL;
2907                 goto __error;
2908         }
2909
2910         /* check mutual exclusivity of File and Config */
2911         if (file && config) {
2912                 snd_error(UCM, "both File and Config specified in SectionUseCase");
2913                 err = -EINVAL;
2914                 goto __error;
2915         }
2916
2917         if (!variant) {
2918                 snd_debug(UCM, "use_case_name %s file '%s'", use_case_name, file);
2919
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");
2923                         err = -EINVAL;
2924                         goto __error;
2925                 }
2926
2927                 /* parse verb from file or config */
2928                 if (file) {
2929                         snd_config_t *cfg;
2930                         /* load config from file */
2931                         err = uc_mgr_config_load_file(uc_mgr, file, &cfg);
2932                         if (err < 0)
2933                                 goto __error;
2934                         /* parse the config */
2935                         err = parse_verb_config(uc_mgr, use_case_name, comment, cfg, file);
2936                         snd_config_delete(cfg);
2937                 } else {
2938                         /* inline config - parse directly */
2939                         err = parse_verb_config(uc_mgr, use_case_name, comment, config,
2940                                                 comment ? comment : use_case_name);
2941                 }
2942         } else {
2943                 /* parse variants */
2944                 struct list_head orig_variable_list;
2945                 snd_config_t *orig_macros = NULL;
2946                 int first_iteration = 1;
2947
2948                 /* save original variable list */
2949                 err = uc_mgr_duplicate_variables(&orig_variable_list, &uc_mgr->variable_list);
2950                 if (err < 0)
2951                         goto __error;
2952
2953                 /* save original macros */
2954                 if (uc_mgr->macros) {
2955                         err = snd_config_copy(&orig_macros, uc_mgr->macros);
2956                         if (err < 0)
2957                                 goto __variant_error;
2958                 }
2959
2960                 snd_config_for_each(i, next, variant) {
2961                         char *vfile, *vcomment;
2962                         const char *id;
2963
2964                         /* restore variables and macros for second and later iterations */
2965                         if (!first_iteration) {
2966                                 uc_mgr_free_value(&uc_mgr->variable_list);
2967
2968                                 err = uc_mgr_duplicate_variables(&uc_mgr->variable_list, &orig_variable_list);
2969                                 if (err < 0)
2970                                         goto __variant_error;
2971
2972                                 if (uc_mgr->macros) {
2973                                         snd_config_delete(uc_mgr->macros);
2974                                         uc_mgr->macros = NULL;
2975                                 }
2976                                 if (orig_macros) {
2977                                         err = snd_config_copy(&uc_mgr->macros, orig_macros);
2978                                         if (err < 0)
2979                                                 goto __variant_error;
2980                                 }
2981                         }
2982                         first_iteration = 0;
2983
2984                         n = snd_config_iterator_entry(i);
2985                         if (snd_config_get_id(n, &id) < 0)
2986                                 continue;
2987                         if (!parse_is_name_safe(id)) {
2988                                 err = -EINVAL;
2989                                 goto __variant_error;
2990                         }
2991                         err = parse_variant(uc_mgr, n, &vfile, &vcomment);
2992                         if (err < 0)
2993                                 break;
2994                         uc_mgr->parse_variant = id;
2995                         if (vfile || file) {
2996                                 snd_config_t *cfg;
2997                                 const char *fname = vfile ? vfile : file;
2998                                 /* load config from file */
2999                                 err = uc_mgr_config_load_file(uc_mgr, fname, &cfg);
3000                                 if (err >= 0) {
3001                                         err = parse_verb_config(uc_mgr, id,
3002                                                                 vcomment ? vcomment : comment,
3003                                                                 cfg, fname);
3004                                         snd_config_delete(cfg);
3005                                 }
3006                         } else {
3007                                 /* inline config from variant */
3008                                 err = parse_verb_config(uc_mgr, id,
3009                                                         vcomment ? vcomment : comment,
3010                                                         config,
3011                                                         vcomment ? vcomment : (comment ? comment : id));
3012                         }
3013                         uc_mgr->parse_variant = NULL;
3014                         free(vfile);
3015                         free(vcomment);
3016                         if (err < 0)
3017                                 break;
3018                 }
3019
3020 __variant_error:
3021                 uc_mgr_free_value(&orig_variable_list);
3022                 if (orig_macros)
3023                         snd_config_delete(orig_macros);
3024         }
3025
3026 __error:
3027         free(use_case_name);
3028         free(file);
3029         free(comment);
3030         return err;
3031 }
3032
3033 /*
3034  * parse controls which should be run only at initial boot (forcefully)
3035  */
3036 static int parse_controls_fixedboot(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
3037 {
3038         int err;
3039
3040         if (!list_empty(&uc_mgr->fixedboot_list)) {
3041                 snd_error(UCM, "FixedBoot list is not empty");
3042                 return -EINVAL;
3043         }
3044         err = parse_sequence(uc_mgr, &uc_mgr->fixedboot_list, cfg);
3045         if (err < 0) {
3046                 snd_error(UCM, "Unable to parse FixedBootSequence");
3047                 return err;
3048         }
3049
3050         return 0;
3051 }
3052
3053 /*
3054  * parse controls which should be run only at initial boot
3055  */
3056 static int parse_controls_boot(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
3057 {
3058         int err;
3059
3060         if (!list_empty(&uc_mgr->boot_list)) {
3061                 snd_error(UCM, "Boot list is not empty");
3062                 return -EINVAL;
3063         }
3064         err = parse_sequence(uc_mgr, &uc_mgr->boot_list, cfg);
3065         if (err < 0) {
3066                 snd_error(UCM, "Unable to parse BootSequence");
3067                 return err;
3068         }
3069
3070         return 0;
3071 }
3072
3073 /*
3074  * parse controls
3075  */
3076 static int parse_controls(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
3077 {
3078         int err;
3079
3080         if (!list_empty(&uc_mgr->default_list)) {
3081                 snd_error(UCM, "Default list is not empty");
3082                 return -EINVAL;
3083         }
3084         err = parse_sequence(uc_mgr, &uc_mgr->default_list, cfg);
3085         if (err < 0) {
3086                 snd_error(UCM, "Unable to parse SectionDefaults");
3087                 return err;
3088         }
3089
3090         return 0;
3091 }
3092
3093 /*
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.
3096  *
3097  * #Example master file for blah sound card
3098  * #By Joe Blogs <joe@bloggs.org>
3099  *
3100  * Comment "Nice Abstracted Soundcard"
3101  *
3102  * # The file is divided into Use case sections. One section per use case verb.
3103  *
3104  * SectionUseCase."Voice Call" {
3105  *      File "voice_call_blah"
3106  *      Comment "Make a voice phone call."
3107  * }
3108  *
3109  * SectionUseCase."HiFi" {
3110  *      File "hifi_blah"
3111  *      Comment "Play and record HiFi quality Music."
3112  * }
3113  *
3114  * # Define Value defaults
3115  *
3116  * ValueDefaults {
3117  *      PlaybackCTL "hw:CARD=0"
3118  *      CaptureCTL "hw:CARD=0"
3119  * }
3120  *
3121  * # The initial boot (run once) configuration.
3122  *
3123  * BootSequence [
3124  *      cset "name='Master Playback Switch',index=2 1,1"
3125  *      cset "name='Master Playback Volume',index=2 25,25"
3126  * ]
3127  *
3128  * # This file also stores the default sound card state.
3129  *
3130  * SectionDefaults [
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"
3135  *      msleep 50
3136  *      ........
3137  * ]
3138  *
3139  * # End of example file.
3140  */
3141 static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
3142 {
3143         snd_config_iterator_t i, next;
3144         snd_config_t *n;
3145         const char *id;
3146         int err;
3147
3148         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
3149                 snd_error(UCM, "compound type expected for master file");
3150                 return -EINVAL;
3151         }
3152
3153         if (uc_mgr->conf_format >= 2) {
3154                 err = parse_syntax_field(uc_mgr, cfg, uc_mgr->conf_file_name);
3155                 if (err < 0)
3156                         return err;
3157         }
3158
3159         /* in-place evaluation */
3160         err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
3161         if (err < 0)
3162                 return err;
3163
3164         /* parse ValueGlobals first */
3165         err = snd_config_search(cfg, "ValueGlobals", &n);
3166         if (err == 0) {
3167                 err = parse_value(uc_mgr, &uc_mgr->global_value_list, n);
3168                 if (err < 0) {
3169                         snd_error(UCM, "failed to parse ValueGlobals");
3170                         return err;
3171                 }
3172         }
3173
3174         err = uc_mgr_check_value(&uc_mgr->global_value_list, "BootCardGroup");
3175         if (err == 0) {
3176                 uc_mgr->card_group = true;
3177                 /* if we are in boot, skip the main parsing loop */
3178                 if (uc_mgr->in_boot)
3179                         return 0;
3180         }
3181
3182         /* parse master config sections */
3183         snd_config_for_each(i, next, cfg) {
3184
3185                 n = snd_config_iterator_entry(i);
3186                 if (snd_config_get_id(n, &id) < 0)
3187                         continue;
3188
3189                 if (strcmp(id, "Comment") == 0) {
3190                         err = parse_string_substitute3(uc_mgr, n, &uc_mgr->comment);
3191                         if (err < 0) {
3192                                 snd_error(UCM, "failed to get master comment");
3193                                 return err;
3194                         }
3195                         continue;
3196                 }
3197
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,
3202                                              NULL, NULL);
3203                         if (err < 0)
3204                                 return err;
3205                         continue;
3206                 }
3207
3208                 /* find default control values section (force boot sequence only) */
3209                 if (strcmp(id, "FixedBootSequence") == 0) {
3210                         err = parse_controls_fixedboot(uc_mgr, n);
3211                         if (err < 0)
3212                                 return err;
3213                         continue;
3214                 }
3215
3216                 /* find default control values section (first boot only) */
3217                 if (strcmp(id, "BootSequence") == 0) {
3218                         err = parse_controls_boot(uc_mgr, n);
3219                         if (err < 0)
3220                                 return err;
3221                         continue;
3222                 }
3223
3224                 /* find default control values section and parse it */
3225                 if (strcmp(id, "SectionDefaults") == 0) {
3226                         err = parse_controls(uc_mgr, n);
3227                         if (err < 0)
3228                                 return err;
3229                         continue;
3230                 }
3231
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);
3235                         if (err < 0) {
3236                                 snd_error(UCM, "failed to parse ValueDefaults");
3237                                 return err;
3238                         }
3239                         continue;
3240                 }
3241
3242                 /* ValueGlobals is parsed at the top of this function */
3243                 if (strcmp(id, "ValueGlobals") == 0)
3244                         continue;
3245
3246                 /* alsa-lib configuration */
3247                 if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) {
3248                         err = parse_libconfig(uc_mgr, n);
3249                         if (err < 0) {
3250                                 snd_error(UCM, "failed to parse LibraryConfig");
3251                                 return err;
3252                         }
3253                         continue;
3254                 }
3255
3256                 /* error */
3257                 if (strcmp(id, "Error") == 0)
3258                         return error_node(uc_mgr, n);
3259
3260                 /* skip further Syntax value updates (Include) */
3261                 if (strcmp(id, "Syntax") == 0)
3262                         continue;
3263
3264                 snd_error(UCM, "unknown master file field %s", id);
3265         }
3266         return 0;
3267 }
3268
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)
3273 {
3274         struct ctl_list *ctl_list;
3275         int err;
3276
3277         err = uc_mgr_open_ctl(mgr, &ctl_list, ctl_name, 0);
3278         if (err < 0)
3279                 return err;
3280
3281         if (info)
3282                 *info = ctl_list->ctl_info;
3283         return err;
3284 }
3285
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)
3288 {
3289         int card, err;
3290         snd_ctl_card_info_t *info;
3291         const char *_driver, *_name, *_long_name;
3292
3293         snd_ctl_card_info_alloca(&info);
3294
3295         card = -1;
3296         if (snd_card_next(&card) < 0 || card < 0) {
3297                 snd_error(UCM, "no soundcards found...");
3298                 return -1;
3299         }
3300
3301         while (card >= 0) {
3302                 char name[32];
3303
3304                 /* clear the list, keep the only one CTL device */
3305                 uc_mgr_free_ctl_list(mgr);
3306
3307                 sprintf(name, "hw:%d", card);
3308                 err = get_card_info(mgr, name, &info);
3309
3310                 if (err == 0) {
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))
3317                                 return 0;
3318                 }
3319
3320                 if (snd_card_next(&card) < 0) {
3321                         snd_error(UCM, "snd_card_next");
3322                         break;
3323                 }
3324         }
3325
3326         uc_mgr_free_ctl_list(mgr);
3327
3328         return -1;
3329 }
3330
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)
3333 {
3334         return get_card_info(mgr, ctl_name, NULL);
3335 }
3336
3337 static int parse_toplevel_path(snd_use_case_mgr_t *uc_mgr,
3338                                char *filename,
3339                                snd_config_t *cfg)
3340 {
3341         snd_config_iterator_t i, next, i2, next2;
3342         snd_config_t *n, *n2;
3343         const char *id;
3344         char *dir = NULL, *file = NULL, fn[PATH_MAX];
3345         struct stat64 st;
3346         long version;
3347         int err;
3348
3349         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
3350                 snd_error(UCM, "compound type expected for UseCasePath node");
3351                 return -EINVAL;
3352         }
3353
3354         /* parse use case path config sections */
3355         snd_config_for_each(i, next, cfg) {
3356                 n = snd_config_iterator_entry(i);
3357
3358                 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
3359                         snd_error(UCM, "compound type expected for UseCasePath.something node");
3360                         return -EINVAL;
3361                 }
3362
3363                         if (snd_config_get_id(n, &id) < 0)
3364                                 continue;
3365
3366                 version = 2;
3367
3368                 /* parse use case path config sections */
3369                 snd_config_for_each(i2, next2, n) {
3370
3371                         n2 = snd_config_iterator_entry(i2);
3372                         if (snd_config_get_id(n2, &id) < 0)
3373                                 continue;
3374
3375                         if (strcmp(id, "Version") == 0) {
3376                                 err = parse_integer_substitute(uc_mgr, n2, &version);
3377                                 if (err < 0) {
3378                                         snd_error(UCM, "unable to parse UcmDirectory");
3379                                         goto __error;
3380                                 }
3381                                 if (version < 1 || version > 2) {
3382                                         snd_error(UCM, "Version must be 1 or 2");
3383                                         err = -EINVAL;
3384                                         goto __error;
3385                                 }
3386                                 continue;
3387                         }
3388
3389                         if (strcmp(id, "Directory") == 0) {
3390                                 err = parse_string_substitute(uc_mgr, n2, &dir);
3391                                 if (err < 0) {
3392                                         snd_error(UCM, "unable to parse Directory");
3393                                         goto __error;
3394                                 }
3395                                 continue;
3396                         }
3397
3398                         if (strcmp(id, "File") == 0) {
3399                                 err = parse_string_substitute(uc_mgr, n2, &file);
3400                                 if (err < 0) {
3401                                         snd_error(UCM, "unable to parse File");
3402                                         goto __error;
3403                                 }
3404                                 continue;
3405                         }
3406
3407                         snd_error(UCM, "unknown UseCasePath field %s", id);
3408                 }
3409
3410                 if (dir == NULL) {
3411                         snd_error(UCM, "Directory is not defined in %s!", filename);
3412                         goto __next;
3413                 }
3414                 if (file == NULL) {
3415                         snd_error(UCM, "File is not defined in %s!", filename);
3416                         goto __next;
3417                 }
3418
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)) {
3423                                 ssize_t r;
3424                                 char *link, *dir2, *p;
3425
3426                                 link = malloc(PATH_MAX);
3427                                 if (link == NULL)
3428                                         goto __enomem;
3429                                 r = readlink(fn, link, PATH_MAX - 1);
3430                                 if (r <= 0) {
3431                                         free(link);
3432                                         goto __next;
3433                                 }
3434                                 link[r] = '\0';
3435                                 p = strrchr(link, '/');
3436                                 if (p) {
3437                                         *p = '\0';
3438                                         dir2 = malloc(PATH_MAX);
3439                                         if (dir2 == NULL) {
3440                                                 free(link);
3441                                                 goto __enomem;
3442                                         }
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';
3447                                         free(dir);
3448                                         dir = dir2;
3449                                 }
3450                                 free(link);
3451                         }
3452                         snd_trace(UCM, "using directory '%s' and file '%s'", dir, file);
3453                         if (replace_string(&uc_mgr->conf_dir_name, dir) == NULL)
3454                                 goto __enomem;
3455                         if (replace_string(&uc_mgr->conf_file_name, file) == NULL)
3456                                 goto __enomem;
3457                         strncpy(filename, fn, PATH_MAX);
3458                         filename[PATH_MAX - 1] = '\0';
3459                         uc_mgr->conf_format = version;
3460                         goto __ok;
3461                 }
3462
3463 __next:
3464                 free(file);
3465                 if (dir != fn)
3466                         free(dir);
3467                 dir = NULL;
3468                 file = NULL;
3469         }
3470
3471         err = -ENOENT;
3472         goto __error;
3473
3474 __enomem:
3475         err = -ENOMEM;
3476         goto __error;
3477
3478 __ok:
3479         err = 0;
3480 __error:
3481         free(file);
3482         if (dir != fn)
3483                 free(dir);
3484         return err;
3485 }
3486
3487 static int parse_toplevel_config(snd_use_case_mgr_t *uc_mgr,
3488                                  char *filename,
3489                                  snd_config_t *cfg)
3490 {
3491         snd_config_iterator_t i, next;
3492         snd_config_t *n;
3493         const char *id;
3494         int err;
3495
3496         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
3497                 snd_error(UCM, "compound type expected for toplevel file");
3498                 return -EINVAL;
3499         }
3500
3501         err = parse_syntax_field(uc_mgr, cfg, filename);
3502         if (err < 0)
3503                 return err;
3504
3505         /* in-place evaluation */
3506         err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
3507         if (err < 0)
3508                 return err;
3509
3510         /* parse toplevel config sections */
3511         snd_config_for_each(i, next, cfg) {
3512
3513                 n = snd_config_iterator_entry(i);
3514                 if (snd_config_get_id(n, &id) < 0)
3515                         continue;
3516
3517                 if (strcmp(id, "UseCasePath") == 0) {
3518                         err = parse_toplevel_path(uc_mgr, filename, n);
3519                         if (err == 0)
3520                                 return err;
3521                         continue;
3522                 }
3523
3524                 /* alsa-lib configuration */
3525                 if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) {
3526                         err = parse_libconfig(uc_mgr, n);
3527                         if (err < 0) {
3528                                 snd_error(UCM, "failed to parse LibConfig");
3529                                 return err;
3530                         }
3531                         continue;
3532                 }
3533
3534                 /* skip further Syntax value updates (Include) */
3535                 if (strcmp(id, "Syntax") == 0)
3536                         continue;
3537
3538                 snd_error(UCM, "unknown toplevel field %s", id);
3539         }
3540
3541         return -ENOENT;
3542 }
3543
3544 static int load_toplevel_config(snd_use_case_mgr_t *uc_mgr,
3545                                 snd_config_t **cfg)
3546 {
3547         char filename[PATH_MAX];
3548         snd_config_t *tcfg;
3549         int err;
3550
3551         ucm_filename(filename, sizeof(filename), 2, NULL, "ucm.conf");
3552
3553         if (access(filename, R_OK) != 0) {
3554                 snd_error(UCM, "Unable to find the top-level configuration file '%s'.", filename);
3555                 return -ENOENT;
3556         }
3557
3558         err = uc_mgr_config_load(2, filename, &tcfg);
3559         if (err < 0)
3560                 goto __error;
3561
3562         /* filename is shared for function input and output! */
3563         err = parse_toplevel_config(uc_mgr, filename, tcfg);
3564         snd_config_delete(tcfg);
3565         if (err < 0)
3566                 goto __error;
3567
3568         err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg);
3569         if (err < 0) {
3570                 snd_error(UCM, "could not parse configuration for card %s", uc_mgr->card_name);
3571                 goto __error;
3572         }
3573
3574         return 0;
3575
3576 __error:
3577         return err;
3578 }
3579
3580 /* load master use case file for sound card based on rules in ucm2/ucm.conf
3581  */
3582 int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
3583 {
3584         snd_config_t *cfg;
3585         const char *name;
3586         int err;
3587
3588         err = snd_config_top(&uc_mgr->local_config);
3589         if (err < 0)
3590                 return err;
3591
3592         err = snd_config_top(&uc_mgr->macros);
3593         if (err < 0)
3594                 return err;
3595
3596         name = uc_mgr->card_name;
3597         if (strncmp(name, "hw:", 3) == 0) {
3598                 err = get_by_card(uc_mgr, name);
3599                 if (err < 0) {
3600                         snd_error(UCM, "card '%s' is not valid", name);
3601                         goto __error;
3602                 }
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);
3607         }
3608
3609         err = load_toplevel_config(uc_mgr, &cfg);
3610         if (err < 0)
3611                 goto __error;
3612
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;
3617         }
3618         snd_config_delete(cfg);
3619         if (err < 0) {
3620                 uc_mgr_free_ctl_list(uc_mgr);
3621                 uc_mgr_free_verb(uc_mgr);
3622         }
3623
3624         return err;
3625
3626 __error:
3627         uc_mgr_free_ctl_list(uc_mgr);
3628         replace_string(&uc_mgr->conf_dir_name, NULL);
3629         return err;
3630 }
3631
3632 static int filename_filter(const struct dirent64 *dirent)
3633 {
3634         if (dirent == NULL)
3635                 return 0;
3636         if (dirent->d_type == DT_DIR) {
3637                 if (dirent->d_name[0] == '.') {
3638                         if (dirent->d_name[1] == '\0')
3639                                 return 0;
3640                         if (dirent->d_name[1] == '.' &&
3641                             dirent->d_name[2] == '\0')
3642                                 return 0;
3643                 }
3644                 return 1;
3645         }
3646         return 0;
3647 }
3648
3649 /* scan all cards and comments
3650  *
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
3655  * component_dir.
3656  */
3657 int uc_mgr_scan_master_configs(const char **_list[])
3658 {
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;
3663         char *s;
3664         snd_config_t *cfg, *c;
3665         int i, j, cnt, err, cards;
3666         long l;
3667         ssize_t ss;
3668         struct dirent64 **namelist;
3669
3670         i = -1;
3671         cards = 0;
3672         while (1) {
3673                 err = snd_card_next(&i);
3674                 if (err < 0)
3675                         return err;
3676                 if (i < 0)
3677                         break;
3678                 cards++;
3679         }
3680         cards += 4;     /* plug-and-play */
3681
3682         if (env)
3683                 snprintf(filename, sizeof(filename), "%s/conf.virt.d", env);
3684         else
3685                 snprintf(filename, sizeof(filename), "%s/ucm2/conf.virt.d",
3686                          snd_config_topdir());
3687
3688 #if defined(_GNU_SOURCE) && \
3689     !defined(__NetBSD__) && \
3690     !defined(__FreeBSD__) && \
3691     !defined(__OpenBSD__) && \
3692     !defined(__DragonFly__) && \
3693     !defined(__sun) && \
3694     !defined(__ANDROID__) && \
3695     !defined(__OHOS__)
3696 #define SORTFUNC        versionsort64
3697 #else
3698 #define SORTFUNC        alphasort64
3699 #endif
3700         err = scandir64(filename, &namelist, filename_filter, SORTFUNC);
3701         if (err < 0) {
3702                 err = -errno;
3703                 snd_error(UCM, "could not scan directory %s: %s", filename, strerror(-err));
3704                 return err;
3705         }
3706         cnt = err;
3707
3708         dfl[0] = '\0';
3709         if (strlen(filename) + 8 < sizeof(filename)) {
3710                 strcat(filename, "/default");
3711                 ss = readlink(filename, dfl, sizeof(dfl)-1);
3712                 if (ss >= 0) {
3713                         dfl[ss] = '\0';
3714                         dfl[sizeof(dfl)-1] = '\0';
3715                         if (dfl[0] && dfl[strlen(dfl)-1] == '/')
3716                                 dfl[strlen(dfl)-1] = '\0';
3717                 } else {
3718                         dfl[0] = '\0';
3719                 }
3720         }
3721
3722         j = 0;
3723         list = calloc(1, (cards + cnt) * 2 * sizeof(char *));
3724         if (list == NULL) {
3725                 err = -ENOMEM;
3726                 goto __err;
3727         }
3728
3729         i = -1;
3730         while (j / 2 < cards) {
3731                 err = snd_card_next(&i);
3732                 if (err < 0)
3733                         goto __err;
3734                 if (i < 0)
3735                         break;
3736                 snprintf(fn, sizeof(fn), "-hw:%d", i);
3737                 err = snd_use_case_mgr_open(&uc_mgr, fn);
3738                 if (err == -ENOENT || err == -ENXIO)
3739                         continue;
3740                 if (err < 0) {
3741                         snd_error(UCM, "Unable to open '%s': %s", fn, snd_strerror(err));
3742                         goto __err;
3743                 }
3744                 err = snd_use_case_get(uc_mgr, "comment", (const char **)&s);
3745                 if (err < 0) {
3746                         err = snd_card_get_longname(i, &s);
3747                         if (err < 0)
3748                                 goto __err;
3749                 }
3750                 snd_use_case_mgr_close(uc_mgr);
3751                 list[j] = strdup(fn + 1);
3752                 if (list[j] == NULL) {
3753                         free(s);
3754                         err = -ENOMEM;
3755                         goto __err;
3756                 }
3757                 list[j + 1] = s;
3758                 j += 2;
3759         }
3760
3761         for (i = 0; i < cnt; i++) {
3762
3763                 d_name = namelist[i]->d_name;
3764
3765                 snprintf(fn, sizeof(fn), "%s.conf", d_name);
3766                 ucm_filename(filename, sizeof(filename), 2, d_name, fn);
3767 #ifdef HAVE_EACCESS
3768                 if (eaccess(filename, R_OK))
3769 #else
3770                 if (access(filename, R_OK))
3771 #endif
3772                         continue;
3773
3774                 err = uc_mgr_config_load(2, filename, &cfg);
3775                 if (err < 0)
3776                         goto __err;
3777                 err = snd_config_search(cfg, "Syntax", &c);
3778                 if (err < 0) {
3779                         snd_error(UCM, "Syntax field not found in %s", d_name);
3780                         snd_config_delete(cfg);
3781                         continue;
3782                 }
3783                 err = snd_config_get_integer(c, &l);
3784                 if (err < 0) {
3785                         snd_error(UCM, "Syntax field is invalid in %s", d_name);
3786                         snd_config_delete(cfg);
3787                         goto __err;
3788                 }
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);
3792                         goto __err;
3793                 }
3794                 err = snd_config_search(cfg, "Comment", &c);
3795                 if (err >= 0) {
3796                         err = parse_string(c, (char **)&list[j+1]);
3797                         if (err < 0) {
3798                                 snd_config_delete(cfg);
3799                                 goto __err;
3800                         }
3801                 }
3802                 snd_config_delete(cfg);
3803                 list[j] = strdup(d_name);
3804                 if (list[j] == NULL) {
3805                         err = -ENOMEM;
3806                         goto __err;
3807                 }
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 *));
3813                         list[0] = save1;
3814                         list[1] = save2;
3815                 }
3816                 j += 2;
3817         }
3818         err = 0;
3819
3820       __err:
3821         for (i = 0; i < cnt; i++)
3822                 free(namelist[i]);
3823         free(namelist);
3824         if (err < 0) {
3825                 for (i = 0; i < j; i++) {
3826                         free((void *)list[i * 2]);
3827                         free((void *)list[i * 2 + 1]);
3828                 }
3829                 free(list);
3830                 return err;
3831         }
3832
3833         *_list = list;
3834         return j;
3835 }