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