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 <limits.h>
38
39 static int filename_filter(const struct dirent64 *dirent);
40
41 static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
42 struct list_head *base,
43 snd_config_t *cfg);
44
45 /*
46 * compose the absolute ucm filename
47 */
ucm_filename(char *fn, size_t fn_len, long version, const char *dir, const char *file)48 static void ucm_filename(char *fn, size_t fn_len, long version,
49 const char *dir, const char *file)
50 {
51 const char *env = getenv(version > 1 ? ALSA_CONFIG_UCM2_VAR : ALSA_CONFIG_UCM_VAR);
52
53 if (file[0] == '/')
54 file++;
55 if (env == NULL)
56 snprintf(fn, fn_len, "%s/%s/%s%s%s",
57 snd_config_topdir(), version > 1 ? "ucm2" : "ucm",
58 dir ?: "", dir ? "/" : "", file);
59 else
60 snprintf(fn, fn_len, "%s/%s%s%s",
61 env, dir ?: "", dir ? "/" : "", file);
62 }
63
64 /*
65 *
66 */
uc_mgr_config_load_file(snd_use_case_mgr_t *uc_mgr, const char *file, snd_config_t **cfg)67 int uc_mgr_config_load_file(snd_use_case_mgr_t *uc_mgr,
68 const char *file, snd_config_t **cfg)
69 {
70 char filename[PATH_MAX];
71 int err;
72
73 ucm_filename(filename, sizeof(filename), uc_mgr->conf_format,
74 file[0] == '/' ? NULL : uc_mgr->conf_dir_name,
75 file);
76 err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg);
77 if (err < 0) {
78 uc_error("error: failed to open file %s: %d", filename, err);
79 return err;
80 }
81 return 0;
82 }
83
84 /*
85 * Replace mallocated string
86 */
replace_string(char **dst, const char *value)87 static char *replace_string(char **dst, const char *value)
88 {
89 free(*dst);
90 *dst = value ? strdup(value) : NULL;
91 return *dst;
92 }
93
94 /*
95 * Parse string
96 */
parse_string(snd_config_t *n, char **res)97 static int parse_string(snd_config_t *n, char **res)
98 {
99 int err;
100
101 err = snd_config_get_string(n, (const char **)res);
102 if (err < 0)
103 return err;
104 *res = strdup(*res);
105 if (*res == NULL)
106 return -ENOMEM;
107 return 0;
108 }
109
110 /*
111 * Parse string and substitute
112 */
parse_string_substitute(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, char **res)113 static int parse_string_substitute(snd_use_case_mgr_t *uc_mgr,
114 snd_config_t *n, char **res)
115 {
116 const char *str;
117 char *s;
118 int err;
119
120 err = snd_config_get_string(n, &str);
121 if (err < 0)
122 return err;
123 err = uc_mgr_get_substituted_value(uc_mgr, &s, str);
124 if (err >= 0)
125 *res = s;
126 return err;
127 }
128
129 /*
130 * Parse string and substitute
131 */
parse_string_substitute3(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, char **res)132 static int parse_string_substitute3(snd_use_case_mgr_t *uc_mgr,
133 snd_config_t *n, char **res)
134 {
135 if (uc_mgr->conf_format < 3)
136 return parse_string(n, res);
137 return parse_string_substitute(uc_mgr, n, res);
138 }
139
140 /*
141 * Parse integer with substitution
142 */
parse_integer_substitute(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, long *res)143 static int parse_integer_substitute(snd_use_case_mgr_t *uc_mgr,
144 snd_config_t *n, long *res)
145 {
146 char *s1, *s2;
147 int err;
148
149 err = snd_config_get_ascii(n, &s1);
150 if (err < 0)
151 return err;
152 err = uc_mgr_get_substituted_value(uc_mgr, &s2, s1);
153 if (err >= 0)
154 err = safe_strtol(s2, res);
155 free(s2);
156 free(s1);
157 return err;
158 }
159
160 /*
161 * Parse integer with substitution
162 */
parse_integer_substitute3(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, long *res)163 static int parse_integer_substitute3(snd_use_case_mgr_t *uc_mgr,
164 snd_config_t *n, long *res)
165 {
166 char *s1, *s2;
167 int err;
168
169 err = snd_config_get_ascii(n, &s1);
170 if (err < 0)
171 return err;
172 if (uc_mgr->conf_format < 3)
173 s2 = s1;
174 else
175 err = uc_mgr_get_substituted_value(uc_mgr, &s2, s1);
176 if (err >= 0)
177 err = safe_strtol(s2, res);
178 if (s1 != s2)
179 free(s2);
180 free(s1);
181 return err;
182 }
183
184 /*
185 * Parse safe ID
186 */
parse_is_name_safe(const char *name)187 static int parse_is_name_safe(const char *name)
188 {
189 if (strchr(name, '.')) {
190 uc_error("char '.' not allowed in '%s'", name);
191 return 0;
192 }
193 return 1;
194 }
195
get_string3(snd_use_case_mgr_t *uc_mgr, const char *s1, char **s)196 static int get_string3(snd_use_case_mgr_t *uc_mgr, const char *s1, char **s)
197 {
198 if (uc_mgr->conf_format < 3) {
199 *s = strdup(s1);
200 if (*s == NULL)
201 return -ENOMEM;
202 return 0;
203 }
204 return uc_mgr_get_substituted_value(uc_mgr, s, s1);
205 }
206
parse_get_safe_name(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, const char *alt, char **name)207 static int parse_get_safe_name(snd_use_case_mgr_t *uc_mgr, snd_config_t *n,
208 const char *alt, char **name)
209 {
210 const char *id;
211 int err;
212
213 if (alt) {
214 id = alt;
215 } else {
216 err = snd_config_get_id(n, &id);
217 if (err < 0)
218 return err;
219 }
220 err = get_string3(uc_mgr, id, name);
221 if (err < 0)
222 return err;
223 if (!parse_is_name_safe(*name)) {
224 free(*name);
225 return -EINVAL;
226 }
227 return 0;
228 }
229
230 /*
231 * Handle 'Error' configuration node.
232 */
error_node(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)233 static int error_node(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
234 {
235 int err;
236 char *s;
237
238 err = parse_string_substitute3(uc_mgr, cfg, &s);
239 if (err < 0) {
240 uc_error("error: failed to get Error string");
241 return err;
242 }
243 if (!uc_mgr->suppress_nodev_errors)
244 uc_error("%s", s);
245 free(s);
246 return -ENXIO;
247 }
248
249 /*
250 *
251 */
parse_syntax_field(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, const char *filename)252 static int parse_syntax_field(snd_use_case_mgr_t *uc_mgr,
253 snd_config_t *cfg, const char *filename)
254 {
255 snd_config_t *n;
256 long l;
257 int err;
258
259 err = snd_config_search(cfg, "Syntax", &n);
260 if (err < 0) {
261 uc_error("Syntax field not found in %s", filename);
262 return -EINVAL;
263 }
264 err = snd_config_get_integer(n, &l);
265 if (err < 0) {
266 uc_error("Syntax field is invalid in %s", filename);
267 return err;
268 }
269 if (l < 2 || l > SYNTAX_VERSION_MAX) {
270 uc_error("Incompatible syntax %ld in %s", l, filename);
271 return -EINVAL;
272 }
273 /* delete this field to optimize strcmp() call in the parsing loop */
274 snd_config_delete(n);
275 uc_mgr->conf_format = l;
276 return l;
277 }
278
279 /*
280 * Evaluate variable regex definitions (in-place delete)
281 */
evaluate_regex(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)282 static int evaluate_regex(snd_use_case_mgr_t *uc_mgr,
283 snd_config_t *cfg)
284 {
285 snd_config_iterator_t i, next;
286 snd_config_t *d, *n;
287 const char *id;
288 int err;
289
290 err = snd_config_search(cfg, "DefineRegex", &d);
291 if (err == -ENOENT)
292 return 1;
293 if (err < 0)
294 return err;
295
296 if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
297 uc_error("compound type expected for DefineRegex");
298 return -EINVAL;
299 }
300
301 if (uc_mgr->conf_format < 3) {
302 uc_error("DefineRegex is supported in v3+ syntax");
303 return -EINVAL;
304 }
305
306 snd_config_for_each(i, next, d) {
307 n = snd_config_iterator_entry(i);
308 err = snd_config_get_id(n, &id);
309 if (err < 0)
310 return err;
311 if (id[0] == '@') {
312 uc_error("error: value names starting with '@' are reserved for application variables");
313 return -EINVAL;
314 }
315 err = uc_mgr_define_regex(uc_mgr, id, n);
316 if (err < 0)
317 return err;
318 }
319
320 snd_config_delete(d);
321 return 0;
322 }
323
324 /*
325 * Evaluate variable definitions (in-place delete)
326 */
evaluate_define(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)327 static int evaluate_define(snd_use_case_mgr_t *uc_mgr,
328 snd_config_t *cfg)
329 {
330 snd_config_iterator_t i, next;
331 snd_config_t *d, *n;
332 const char *id;
333 char *var, *s;
334 int err;
335
336 err = snd_config_search(cfg, "Define", &d);
337 if (err == -ENOENT)
338 return evaluate_regex(uc_mgr, cfg);
339 if (err < 0)
340 return err;
341
342 if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
343 uc_error("compound type expected for Define");
344 return -EINVAL;
345 }
346
347 if (uc_mgr->conf_format < 3) {
348 uc_error("Define is supported in v3+ syntax");
349 return -EINVAL;
350 }
351
352 snd_config_for_each(i, next, d) {
353 n = snd_config_iterator_entry(i);
354 err = snd_config_get_id(n, &id);
355 if (err < 0)
356 return err;
357 err = snd_config_get_ascii(n, &var);
358 if (err < 0)
359 return err;
360 err = uc_mgr_get_substituted_value(uc_mgr, &s, var);
361 free(var);
362 if (err < 0)
363 return err;
364 if (id[0] == '@') {
365 free(s);
366 uc_error("error: value names starting with '@' are reserved for application variables");
367 return -EINVAL;
368 }
369 err = uc_mgr_set_variable(uc_mgr, id, s);
370 free(s);
371 if (err < 0)
372 return err;
373 }
374
375 snd_config_delete(d);
376
377 return evaluate_regex(uc_mgr, cfg);
378 }
379
380 /*
381 * Evaluate macro definitions (in-place delete)
382 */
evaluate_define_macro(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)383 static int evaluate_define_macro(snd_use_case_mgr_t *uc_mgr,
384 snd_config_t *cfg)
385 {
386 snd_config_t *d;
387 int err;
388
389 err = snd_config_search(cfg, "DefineMacro", &d);
390 if (err == -ENOENT)
391 return 1;
392 if (err < 0)
393 return err;
394
395 if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
396 uc_error("compound type expected for DefineMacro");
397 return -EINVAL;
398 }
399
400 if (uc_mgr->conf_format < 6) {
401 uc_error("DefineMacro is supported in v6+ syntax");
402 return -EINVAL;
403 }
404
405 err = snd_config_merge(uc_mgr->macros, d, 0);
406 if (err < 0)
407 return err;
408 return 0;
409 }
410
evaluate_macro1(snd_use_case_mgr_t *uc_mgr, snd_config_t *dst, snd_config_t *args)411 static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr,
412 snd_config_t *dst,
413 snd_config_t *args)
414 {
415 snd_config_iterator_t i, next;
416 snd_config_t *m, *mc, *a, *n;
417 const char *mid, *id;
418 char name[128], *var;
419 const char *s;
420 int err;
421
422 err = snd_config_get_id(args, &mid);
423 if (err < 0)
424 return err;
425 err = snd_config_search(uc_mgr->macros, mid, &m);
426 if (err < 0) {
427 uc_error("Macro '%s' is not defined", mid);
428 return err;
429 }
430
431 a = args;
432 if (snd_config_get_type(args) == SND_CONFIG_TYPE_STRING) {
433 err = snd_config_get_string(args, &s);
434 if (err < 0)
435 return err;
436 err = snd_config_load_string(&a, s, 0);
437 if (err < 0)
438 return err;
439 } else if (snd_config_get_type(args) != SND_CONFIG_TYPE_COMPOUND) {
440 return -EINVAL;
441 }
442
443 /* set arguments */
444 snd_config_for_each(i, next, a) {
445 n = snd_config_iterator_entry(i);
446 err = snd_config_get_id(n, &id);
447 if (err < 0)
448 goto __err_path;
449 err = snd_config_get_ascii(n, &var);
450 if (err < 0)
451 goto __err_path;
452 snprintf(name, sizeof(name), "__%s", id);
453 err = uc_mgr_set_variable(uc_mgr, name, var);
454 free(var);
455 if (err < 0)
456 goto __err_path;
457 }
458
459 /* merge + substitute variables */
460 err = snd_config_copy(&mc, m);
461 if (err < 0)
462 goto __err_path;
463 err = uc_mgr_evaluate_inplace(uc_mgr, mc);
464 if (err < 0) {
465 snd_config_delete(mc);
466 goto __err_path;
467 }
468 err = uc_mgr_config_tree_merge(uc_mgr, dst, mc, NULL, NULL);
469 snd_config_delete(mc);
470
471 /* delete arguments */
472 snd_config_for_each(i, next, a) {
473 n = snd_config_iterator_entry(i);
474 err = snd_config_get_id(n, &id);
475 if (err < 0)
476 goto __err_path;
477 snprintf(name, sizeof(name), "__%s", id);
478 err = uc_mgr_delete_variable(uc_mgr, name);
479 if (err < 0)
480 goto __err_path;
481 }
482
483 __err_path:
484 if (a != args)
485 snd_config_delete(a);
486 return err;
487 }
488
489 /*
490 * Evaluate macro definitions and instances (in-place delete)
491 */
evaluate_macro(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)492 static int evaluate_macro(snd_use_case_mgr_t *uc_mgr,
493 snd_config_t *cfg)
494 {
495 snd_config_iterator_t i, i2, next, next2;
496 snd_config_t *d, *n, *n2;
497 int err, ret;
498
499 ret = evaluate_define_macro(uc_mgr, cfg);
500 if (ret < 0)
501 return ret;
502
503 err = snd_config_search(cfg, "Macro", &d);
504 if (err == -ENOENT)
505 return ret;
506 if (err < 0)
507 return err;
508
509 if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
510 uc_error("compound type expected for DefineMacro");
511 return -EINVAL;
512 }
513
514 if (uc_mgr->conf_format < 6) {
515 uc_error("Macro is supported in v6+ syntax");
516 return -EINVAL;
517 }
518
519 snd_config_for_each(i, next, d) {
520 n = snd_config_iterator_entry(i);
521 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
522 const char *id;
523 if (snd_config_get_id(n, &id))
524 id = "";
525 uc_error("compound type expected for Macro.%s", id);
526 return -EINVAL;
527 }
528 snd_config_for_each(i2, next2, n) {
529 n2 = snd_config_iterator_entry(i2);
530 err = evaluate_macro1(uc_mgr, cfg, n2);
531 if (err < 0)
532 return err;
533 }
534 }
535
536 snd_config_delete(d);
537
538 return 0;
539 }
540
541 /*
542 * Evaluate include (in-place)
543 */
evaluate_include(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)544 static int evaluate_include(snd_use_case_mgr_t *uc_mgr,
545 snd_config_t *cfg)
546 {
547 snd_config_t *n;
548 int err;
549
550 err = snd_config_search(cfg, "Include", &n);
551 if (err == -ENOENT)
552 return 1;
553 if (err < 0)
554 return err;
555
556 err = uc_mgr_evaluate_include(uc_mgr, cfg, n);
557 snd_config_delete(n);
558 return err;
559 }
560
561 /*
562 * Evaluate condition (in-place)
563 */
evaluate_condition(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)564 static int evaluate_condition(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
565 {
566 snd_config_t *n;
567 int err;
568
569 err = snd_config_search(cfg, "If", &n);
570 if (err == -ENOENT)
571 return 1;
572 if (err < 0)
573 return err;
574
575 err = uc_mgr_evaluate_condition(uc_mgr, cfg, n);
576 snd_config_delete(n);
577 return err;
578 }
579
580 /*
581 * Evaluate variant (in-place)
582 */
evaluate_variant(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)583 static int evaluate_variant(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
584 {
585 snd_config_iterator_t i, next;
586 snd_config_t *n, *c;
587 const char *id;
588 int err;
589
590 err = snd_config_search(cfg, "Variant", &c);
591 if (err == -ENOENT)
592 return 1;
593 if (err < 0)
594 return err;
595
596 if (uc_mgr->conf_format < 6) {
597 uc_error("Variant is supported in v6+ syntax");
598 return -EINVAL;
599 }
600
601 if (uc_mgr->parse_master_section)
602 return 1;
603
604 if (uc_mgr->parse_variant == NULL)
605 goto __ret;
606
607 snd_config_for_each(i, next, c) {
608 n = snd_config_iterator_entry(i);
609
610 if (snd_config_get_id(n, &id) < 0)
611 return -EINVAL;
612
613 if (strcmp(id, uc_mgr->parse_variant))
614 continue;
615
616 err = uc_mgr_evaluate_inplace(uc_mgr, n);
617 if (err < 0)
618 return err;
619
620 err = uc_mgr_config_tree_merge(uc_mgr, cfg, n, NULL, NULL);
621 if (err < 0)
622 return err;
623 snd_config_delete(c);
624 return 0;
625 }
626
627 __ret:
628 snd_config_delete(c);
629 return 1;
630 }
631
632 /*
633 * In-place evaluate
634 */
uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)635 int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
636 snd_config_t *cfg)
637 {
638 long iterations = 10000;
639 int err1 = 0, err2 = 0, err3 = 0, err4 = 0, err5 = 0;
640
641 while (err1 == 0 || err2 == 0 || err3 == 0 || err4 == 0 || err5 == 0) {
642 if (iterations == 0) {
643 uc_error("Maximal inplace evaluation iterations number reached (recursive references?)");
644 return -EINVAL;
645 }
646 iterations--;
647 /* variables at first */
648 err1 = evaluate_define(uc_mgr, cfg);
649 if (err1 < 0)
650 return err1;
651 /* include at second */
652 err2 = evaluate_include(uc_mgr, cfg);
653 if (err2 < 0)
654 return err2;
655 /* include or macro may define another variables */
656 /* conditions may depend on them */
657 if (err2 == 0)
658 continue;
659 err3 = evaluate_variant(uc_mgr, cfg);
660 if (err3 < 0)
661 return err3;
662 if (err3 == 0)
663 continue;
664 uc_mgr->macro_hops++;
665 if (uc_mgr->macro_hops > 100) {
666 uc_error("Maximal macro hops reached!");
667 return -EINVAL;
668 }
669 err4 = evaluate_macro(uc_mgr, cfg);
670 uc_mgr->macro_hops--;
671 if (err4 < 0)
672 return err4;
673 if (err4 == 0)
674 continue;
675 err5 = evaluate_condition(uc_mgr, cfg);
676 if (err5 < 0)
677 return err5;
678 }
679 return 0;
680 }
681
682 /*
683 * Parse one item for alsa-lib config
684 */
parse_libconfig1(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)685 static int parse_libconfig1(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
686 {
687 snd_config_iterator_t i, next;
688 snd_config_t *n, *config = NULL;
689 const char *id, *file = NULL;
690 bool substfile = false, substconfig = false;
691 int err;
692
693 if (snd_config_get_id(cfg, &id) < 0)
694 return -EINVAL;
695
696 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
697 uc_error("compound type expected for %s", id);
698 return -EINVAL;
699 }
700
701 snd_config_for_each(i, next, cfg) {
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, "File") == 0 ||
708 strcmp(id, "SubstiFile") == 0) {
709 substfile = id[0] == 'S';
710 err = snd_config_get_string(n, &file);
711 if (err < 0)
712 return err;
713 continue;
714 }
715
716 if (strcmp(id, "Config") == 0 ||
717 strcmp(id, "SubstiConfig") == 0) {
718 substconfig = id[0] == 'S';
719 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND)
720 return -EINVAL;
721 config = n;
722 continue;
723 }
724
725 uc_error("unknown field %s", id);
726 return -EINVAL;
727 }
728
729 if (file) {
730 if (substfile) {
731 snd_config_t *cfg;
732 err = uc_mgr_config_load(uc_mgr->conf_format, file, &cfg);
733 if (err < 0)
734 return err;
735 err = uc_mgr_substitute_tree(uc_mgr, cfg);
736 if (err < 0) {
737 snd_config_delete(cfg);
738 return err;
739 }
740 err = snd_config_merge(uc_mgr->local_config, cfg, 0);
741 if (err < 0) {
742 snd_config_delete(cfg);
743 return err;
744 }
745 } else {
746 char filename[PATH_MAX];
747
748 ucm_filename(filename, sizeof(filename), uc_mgr->conf_format,
749 file[0] == '/' ? NULL : uc_mgr->conf_dir_name,
750 file);
751 err = uc_mgr_config_load_into(uc_mgr->conf_format, filename, uc_mgr->local_config);
752 if (err < 0)
753 return err;
754 }
755 }
756
757 if (config) {
758 if (substconfig) {
759 err = uc_mgr_substitute_tree(uc_mgr, config);
760 if (err < 0)
761 return err;
762 }
763 err = snd_config_merge(uc_mgr->local_config, config, 0);
764 if (err < 0)
765 return err;
766 }
767
768 return 0;
769 }
770
771 /*
772 * Parse alsa-lib config
773 */
parse_libconfig(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)774 static int parse_libconfig(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
775 {
776 snd_config_iterator_t i, next;
777 snd_config_t *n;
778 const char *id;
779 int err;
780
781 if (snd_config_get_id(cfg, &id) < 0)
782 return -EINVAL;
783
784 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
785 uc_error("compound type expected for %s", id);
786 return -EINVAL;
787 }
788
789 snd_config_for_each(i, next, cfg) {
790 n = snd_config_iterator_entry(i);
791
792 err = parse_libconfig1(uc_mgr, n);
793 if (err < 0)
794 return err;
795 }
796
797 return 0;
798 }
799
800 /*
801 * Parse transition
802 */
parse_transition(snd_use_case_mgr_t *uc_mgr, struct list_head *tlist, snd_config_t *cfg)803 static int parse_transition(snd_use_case_mgr_t *uc_mgr,
804 struct list_head *tlist,
805 snd_config_t *cfg)
806 {
807 struct transition_sequence *tseq;
808 const char *id;
809 snd_config_iterator_t i, next;
810 snd_config_t *n;
811 int err;
812
813 if (snd_config_get_id(cfg, &id) < 0)
814 return -EINVAL;
815
816 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
817 uc_error("compound type expected for %s", id);
818 return -EINVAL;
819 }
820
821 snd_config_for_each(i, next, cfg) {
822 n = snd_config_iterator_entry(i);
823
824 if (snd_config_get_id(n, &id) < 0)
825 return -EINVAL;
826
827 tseq = calloc(1, sizeof(*tseq));
828 if (tseq == NULL)
829 return -ENOMEM;
830 INIT_LIST_HEAD(&tseq->transition_list);
831
832 err = get_string3(uc_mgr, id, &tseq->name);
833 if (err < 0) {
834 free(tseq);
835 return err;
836 }
837
838 err = parse_sequence(uc_mgr, &tseq->transition_list, n);
839 if (err < 0) {
840 uc_mgr_free_transition_element(tseq);
841 return err;
842 }
843
844 list_add(&tseq->list, tlist);
845 }
846 return 0;
847 }
848
849 /*
850 * Parse compound
851 */
parse_compound(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *), void *data1, void *data2)852 static int parse_compound(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
853 int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
854 void *data1, void *data2)
855 {
856 const char *id;
857 snd_config_iterator_t i, next;
858 snd_config_t *n;
859 int err;
860
861 if (snd_config_get_id(cfg, &id) < 0)
862 return -EINVAL;
863
864 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
865 uc_error("compound type expected for %s", id);
866 return -EINVAL;
867 }
868 /* parse compound */
869 snd_config_for_each(i, next, cfg) {
870 n = snd_config_iterator_entry(i);
871
872 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
873 uc_error("compound type expected for %s, is %d", id, snd_config_get_type(cfg));
874 return -EINVAL;
875 }
876
877 err = fcn(uc_mgr, n, data1, data2);
878 if (err < 0)
879 return err;
880 }
881
882 return 0;
883 }
884
strip_legacy_dev_index(char *name)885 static int strip_legacy_dev_index(char *name)
886 {
887 char *dot = strchr(name, '.');
888 if (!dot)
889 return 0;
890 if (dot[1] != '0' || dot[2] != '\0') {
891 uc_error("device name %s contains a '.',"
892 " and is not legacy foo.0 format", name);
893 return -EINVAL;
894 }
895 *dot = '\0';
896 return 0;
897 }
898
899 /*
900 * Parse device list
901 */
parse_device_list(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, struct dev_list *dev_list, enum dev_list_type type, snd_config_t *cfg)902 static int parse_device_list(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
903 struct dev_list *dev_list,
904 enum dev_list_type type,
905 snd_config_t *cfg)
906 {
907 struct dev_list_node *sdev;
908 const char *id;
909 snd_config_iterator_t i, next;
910 snd_config_t *n;
911 int err;
912
913 if (dev_list->type != DEVLIST_NONE) {
914 uc_error("error: multiple supported or"
915 " conflicting device lists");
916 return -EEXIST;
917 }
918
919 if (snd_config_get_id(cfg, &id) < 0)
920 return -EINVAL;
921
922 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
923 uc_error("compound type expected for %s", id);
924 return -EINVAL;
925 }
926
927 snd_config_for_each(i, next, cfg) {
928 n = snd_config_iterator_entry(i);
929
930 if (snd_config_get_id(n, &id) < 0)
931 return -EINVAL;
932
933 sdev = calloc(1, sizeof(struct dev_list_node));
934 if (sdev == NULL)
935 return -ENOMEM;
936 err = parse_string_substitute3(uc_mgr, n, &sdev->name);
937 if (err < 0) {
938 free(sdev);
939 return err;
940 }
941 err = strip_legacy_dev_index(sdev->name);
942 if (err < 0) {
943 free(sdev->name);
944 free(sdev);
945 return err;
946 }
947 list_add(&sdev->list, &dev_list->list);
948 }
949
950 dev_list->type = type;
951
952 return 0;
953 }
954
955 /* Find a component device by its name, and remove it from machine device
956 * list.
957 *
958 * Component devices are defined by machine components (usually off-soc
959 * codes or DSP embeded in SoC). Since alsaconf imports their configuration
960 * files automatically, we don't know which devices are component devices
961 * until they are referenced by a machine device sequence. So here when we
962 * find a referenced device, we move it from the machine device list to the
963 * component device list. Component devices will not be exposed to applications
964 * by the original API to list devices for backward compatibility. So sound
965 * servers can only see the machine devices.
966 */
find_component_dev(snd_use_case_mgr_t *uc_mgr, const char *name)967 struct use_case_device *find_component_dev(snd_use_case_mgr_t *uc_mgr,
968 const char *name)
969 {
970 struct list_head *pos, *posdev, *_posdev;
971 struct use_case_verb *verb;
972 struct use_case_device *dev;
973
974 list_for_each(pos, &uc_mgr->verb_list) {
975 verb = list_entry(pos, struct use_case_verb, list);
976
977 /* search in the component device list */
978 list_for_each(posdev, &verb->cmpt_device_list) {
979 dev = list_entry(posdev, struct use_case_device, list);
980 if (!strcmp(dev->name, name))
981 return dev;
982 }
983
984 /* search the machine device list */
985 list_for_each_safe(posdev, _posdev, &verb->device_list) {
986 dev = list_entry(posdev, struct use_case_device, list);
987 if (!strcmp(dev->name, name)) {
988 /* find the component device, move it from the
989 * machine device list to the component device
990 * list.
991 */
992 list_del(&dev->list);
993 list_add_tail(&dev->list,
994 &verb->cmpt_device_list);
995 return dev;
996 }
997 }
998 }
999
1000 return NULL;
1001 }
1002
1003 /* parse sequence of a component device
1004 *
1005 * This function will find the component device and mark if its enable or
1006 * disable sequence is needed by its parenet device.
1007 */
parse_component_seq(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, int enable, struct component_sequence *cmpt_seq)1008 static int parse_component_seq(snd_use_case_mgr_t *uc_mgr,
1009 snd_config_t *n, int enable,
1010 struct component_sequence *cmpt_seq)
1011 {
1012 char *val;
1013 int err;
1014
1015 err = parse_string_substitute3(uc_mgr, n, &val);
1016 if (err < 0)
1017 return err;
1018
1019 cmpt_seq->device = find_component_dev(uc_mgr, val);
1020 if (!cmpt_seq->device) {
1021 uc_error("error: Cannot find component device %s", val);
1022 free(val);
1023 return -EINVAL;
1024 }
1025 free(val);
1026
1027 /* Parent needs its enable or disable sequence */
1028 cmpt_seq->enable = enable;
1029
1030 return 0;
1031 }
1032
1033 /*
1034 * Parse sequences.
1035 *
1036 * Sequence controls elements are in the following form:-
1037 *
1038 * cdev "hw:0"
1039 * cset "element_id_syntax value_syntax"
1040 * usleep time
1041 * exec "any unix command with arguments"
1042 * enadev "component device name"
1043 * disdev "component device name"
1044 *
1045 * e.g.
1046 * cset "name='Master Playback Switch' 0,0"
1047 * cset "iface=PCM,name='Disable HDMI',index=1 0"
1048 * enadev "rt286:Headphones"
1049 * disdev "rt286:Speaker"
1050 */
parse_sequence(snd_use_case_mgr_t *uc_mgr, struct list_head *base, snd_config_t *cfg)1051 static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
1052 struct list_head *base,
1053 snd_config_t *cfg)
1054 {
1055 struct sequence_element *curr;
1056 snd_config_iterator_t i, next;
1057 snd_config_t *n;
1058 int err, idx = 0;
1059 const char *cmd = NULL;
1060
1061 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1062 uc_error("error: compound is expected for sequence definition");
1063 return -EINVAL;
1064 }
1065
1066 snd_config_for_each(i, next, cfg) {
1067 const char *id;
1068 idx ^= 1;
1069 n = snd_config_iterator_entry(i);
1070 err = snd_config_get_id(n, &id);
1071 if (err < 0)
1072 continue;
1073 if (idx == 1) {
1074 if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
1075 uc_error("error: string type is expected for sequence command");
1076 return -EINVAL;
1077 }
1078 snd_config_get_string(n, &cmd);
1079 continue;
1080 }
1081
1082 /* alloc new sequence element */
1083 curr = calloc(1, sizeof(struct sequence_element));
1084 if (curr == NULL)
1085 return -ENOMEM;
1086 list_add_tail(&curr->list, base);
1087
1088 if (strcmp(cmd, "cdev") == 0) {
1089 curr->type = SEQUENCE_ELEMENT_TYPE_CDEV;
1090 err = parse_string_substitute3(uc_mgr, n, &curr->data.cdev);
1091 if (err < 0) {
1092 uc_error("error: cdev requires a string!");
1093 return err;
1094 }
1095 continue;
1096 }
1097
1098 if (strcmp(cmd, "cset") == 0) {
1099 curr->type = SEQUENCE_ELEMENT_TYPE_CSET;
1100 cset:
1101 err = parse_string_substitute3(uc_mgr, n, &curr->data.cset);
1102 if (err < 0) {
1103 uc_error("error: %s requires a string!", cmd);
1104 return err;
1105 }
1106 continue;
1107 }
1108
1109 if (strcmp(cmd, "enadev") == 0 ||
1110 strcmp(cmd, "disdev") == 0) {
1111 /* need to enable or disable a component device */
1112 curr->type = SEQUENCE_ELEMENT_TYPE_CMPT_SEQ;
1113 err = parse_component_seq(uc_mgr, n,
1114 strcmp(cmd, "enadev") == 0,
1115 &curr->data.cmpt_seq);
1116 if (err < 0) {
1117 uc_error("error: %s requires a valid device!", cmd);
1118 return err;
1119 }
1120 continue;
1121 }
1122
1123 if (strcmp(cmd, "enadev2") == 0) {
1124 curr->type = SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ;
1125 goto device;
1126 }
1127
1128 if (strcmp(cmd, "disdev2") == 0) {
1129 curr->type = SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_SEQ;
1130 device:
1131 err = parse_string_substitute3(uc_mgr, n, &curr->data.device);
1132 if (err < 0) {
1133 uc_error("error: %s requires a valid device!", cmd);
1134 return err;
1135 }
1136 continue;
1137 }
1138
1139 if (strcmp(cmd, "disdevall") == 0) {
1140 curr->type = SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_ALL;
1141 continue;
1142 }
1143
1144 if (strcmp(cmd, "cset-bin-file") == 0) {
1145 curr->type = SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE;
1146 goto cset;
1147 }
1148
1149 if (strcmp(cmd, "cset-tlv") == 0) {
1150 curr->type = SEQUENCE_ELEMENT_TYPE_CSET_TLV;
1151 goto cset;
1152 }
1153
1154 if (strcmp(cmd, "cset-new") == 0) {
1155 curr->type = SEQUENCE_ELEMENT_TYPE_CSET_NEW;
1156 goto cset;
1157 }
1158
1159 if (strcmp(cmd, "ctl-remove") == 0) {
1160 curr->type = SEQUENCE_ELEMENT_TYPE_CTL_REMOVE;
1161 goto cset;
1162 }
1163
1164 if (strcmp(cmd, "sysw") == 0) {
1165 curr->type = SEQUENCE_ELEMENT_TYPE_SYSSET;
1166 err = parse_string_substitute3(uc_mgr, n, &curr->data.sysw);
1167 if (err < 0) {
1168 uc_error("error: sysw requires a string!");
1169 return err;
1170 }
1171 continue;
1172 }
1173
1174 if (strcmp(cmd, "usleep") == 0) {
1175 curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
1176 err = parse_integer_substitute3(uc_mgr, n, &curr->data.sleep);
1177 if (err < 0) {
1178 uc_error("error: usleep requires integer!");
1179 return err;
1180 }
1181 continue;
1182 }
1183
1184 if (strcmp(cmd, "msleep") == 0) {
1185 curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
1186 err = parse_integer_substitute3(uc_mgr, n, &curr->data.sleep);
1187 if (err < 0) {
1188 uc_error("error: msleep requires integer!");
1189 return err;
1190 }
1191 curr->data.sleep *= 1000L;
1192 continue;
1193 }
1194
1195 if (strcmp(cmd, "exec") == 0) {
1196 curr->type = SEQUENCE_ELEMENT_TYPE_EXEC;
1197 exec:
1198 err = parse_string_substitute3(uc_mgr, n, &curr->data.exec);
1199 if (err < 0) {
1200 uc_error("error: exec requires a string!");
1201 return err;
1202 }
1203 continue;
1204 }
1205
1206 if (strcmp(cmd, "shell") == 0) {
1207 curr->type = SEQUENCE_ELEMENT_TYPE_SHELL;
1208 goto exec;
1209 }
1210
1211 if (strcmp(cmd, "cfg-save") == 0) {
1212 curr->type = SEQUENCE_ELEMENT_TYPE_CFGSAVE;
1213 err = parse_string_substitute3(uc_mgr, n, &curr->data.cfgsave);
1214 if (err < 0) {
1215 uc_error("error: sysw requires a string!");
1216 return err;
1217 }
1218 continue;
1219 }
1220
1221 if (strcmp(cmd, "comment") == 0)
1222 goto skip;
1223
1224 uc_error("error: sequence command '%s' is ignored", cmd);
1225
1226 skip:
1227 list_del(&curr->list);
1228 uc_mgr_free_sequence_element(curr);
1229 }
1230
1231 return 0;
1232 }
1233
1234 /*
1235 *
1236 */
uc_mgr_add_value(struct list_head *base, const char *key, char *val)1237 int uc_mgr_add_value(struct list_head *base, const char *key, char *val)
1238 {
1239 struct ucm_value *curr;
1240
1241 curr = calloc(1, sizeof(struct ucm_value));
1242 if (curr == NULL)
1243 return -ENOMEM;
1244 curr->name = strdup(key);
1245 if (curr->name == NULL) {
1246 free(curr);
1247 return -ENOMEM;
1248 }
1249 list_add_tail(&curr->list, base);
1250 curr->data = val;
1251 return 0;
1252 }
1253
1254 /*
1255 * Parse values.
1256 *
1257 * Parse values describing PCM, control/mixer settings and stream parameters.
1258 *
1259 * Value {
1260 * TQ Voice
1261 * CapturePCM "hw:1"
1262 * PlaybackVolume "name='Master Playback Volume',index=2"
1263 * PlaybackSwitch "name='Master Playback Switch',index=2"
1264 * }
1265 */
parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, struct list_head *base, snd_config_t *cfg)1266 static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
1267 struct list_head *base,
1268 snd_config_t *cfg)
1269 {
1270 snd_config_iterator_t i, next;
1271 snd_config_t *n;
1272 char *s;
1273 snd_config_type_t type;
1274 int err;
1275
1276 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1277 uc_error("error: compound is expected for value definition");
1278 return -EINVAL;
1279 }
1280
1281 /* in-place evaluation */
1282 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1283 if (err < 0)
1284 return err;
1285
1286 snd_config_for_each(i, next, cfg) {
1287 const char *id;
1288 n = snd_config_iterator_entry(i);
1289 err = snd_config_get_id(n, &id);
1290 if (err < 0)
1291 continue;
1292
1293 type = snd_config_get_type(n);
1294 switch (type) {
1295 case SND_CONFIG_TYPE_INTEGER:
1296 case SND_CONFIG_TYPE_INTEGER64:
1297 case SND_CONFIG_TYPE_REAL:
1298 err = snd_config_get_ascii(n, &s);
1299 if (err < 0) {
1300 uc_error("error: unable to parse value for id '%s': %s!", id, snd_strerror(err));
1301 return err;
1302 }
1303 break;
1304 case SND_CONFIG_TYPE_STRING:
1305 err = parse_string_substitute(uc_mgr, n, &s);
1306 if (err < 0) {
1307 uc_error("error: unable to parse a string for id '%s'!", id);
1308 return err;
1309 }
1310 break;
1311 default:
1312 uc_error("error: invalid type %i in Value compound '%s'", type, id);
1313 return -EINVAL;
1314 }
1315 err = uc_mgr_add_value(base, id, s);
1316 if (err < 0) {
1317 free(s);
1318 return err;
1319 }
1320 }
1321
1322 return 0;
1323 }
1324
1325 /*
1326 * Parse Modifier Use cases
1327 *
1328 * # Each modifier is described in new section. N modifiers are allowed
1329 * SectionModifier."Capture Voice" {
1330 *
1331 * Comment "Record voice call"
1332 *
1333 * SupportedDevice [
1334 * "x"
1335 * "y"
1336 * ]
1337 *
1338 * ConflictingDevice [
1339 * "x"
1340 * "y"
1341 * ]
1342 *
1343 * EnableSequence [
1344 * ....
1345 * ]
1346 *
1347 * DisableSequence [
1348 * ...
1349 * ]
1350 *
1351 * TransitionSequence."ToModifierName" [
1352 * ...
1353 * ]
1354 *
1355 * # Optional TQ and ALSA PCMs
1356 * Value {
1357 * TQ Voice
1358 * CapturePCM "hw:1"
1359 * PlaybackVolume "name='Master Playback Volume',index=2"
1360 * PlaybackSwitch "name='Master Playback Switch',index=2"
1361 * }
1362 * }
1363 *
1364 * SupportedDevice and ConflictingDevice cannot be specified together.
1365 * Both are optional.
1366 */
parse_modifier(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, void *data1, void *data2)1367 static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
1368 snd_config_t *cfg,
1369 void *data1, void *data2)
1370 {
1371 struct use_case_verb *verb = data1;
1372 struct use_case_modifier *modifier;
1373 char *name;
1374 snd_config_iterator_t i, next;
1375 snd_config_t *n;
1376 int err;
1377
1378 if (parse_get_safe_name(uc_mgr, cfg, data2, &name) < 0)
1379 return -EINVAL;
1380
1381 /* allocate modifier */
1382 modifier = calloc(1, sizeof(*modifier));
1383 if (modifier == NULL) {
1384 free(name);
1385 return -ENOMEM;
1386 }
1387 INIT_LIST_HEAD(&modifier->enable_list);
1388 INIT_LIST_HEAD(&modifier->disable_list);
1389 INIT_LIST_HEAD(&modifier->transition_list);
1390 INIT_LIST_HEAD(&modifier->dev_list.list);
1391 INIT_LIST_HEAD(&modifier->value_list);
1392 list_add_tail(&modifier->list, &verb->modifier_list);
1393 modifier->name = name;
1394
1395 /* in-place evaluation */
1396 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1397 if (err < 0)
1398 return err;
1399
1400 snd_config_for_each(i, next, cfg) {
1401 const char *id;
1402 n = snd_config_iterator_entry(i);
1403 if (snd_config_get_id(n, &id) < 0)
1404 continue;
1405
1406 if (strcmp(id, "Comment") == 0) {
1407 err = parse_string_substitute3(uc_mgr, n, &modifier->comment);
1408 if (err < 0) {
1409 uc_error("error: failed to get modifier comment");
1410 return err;
1411 }
1412 continue;
1413 }
1414
1415 if (strcmp(id, "SupportedDevice") == 0) {
1416 err = parse_device_list(uc_mgr, &modifier->dev_list,
1417 DEVLIST_SUPPORTED, n);
1418 if (err < 0) {
1419 uc_error("error: failed to parse supported"
1420 " device list");
1421 return err;
1422 }
1423 }
1424
1425 if (strcmp(id, "ConflictingDevice") == 0) {
1426 err = parse_device_list(uc_mgr, &modifier->dev_list,
1427 DEVLIST_CONFLICTING, n);
1428 if (err < 0) {
1429 uc_error("error: failed to parse conflicting"
1430 " device list");
1431 return err;
1432 }
1433 }
1434
1435 if (strcmp(id, "EnableSequence") == 0) {
1436 err = parse_sequence(uc_mgr, &modifier->enable_list, n);
1437 if (err < 0) {
1438 uc_error("error: failed to parse modifier"
1439 " enable sequence");
1440 return err;
1441 }
1442 continue;
1443 }
1444
1445 if (strcmp(id, "DisableSequence") == 0) {
1446 err = parse_sequence(uc_mgr, &modifier->disable_list, n);
1447 if (err < 0) {
1448 uc_error("error: failed to parse modifier"
1449 " disable sequence");
1450 return err;
1451 }
1452 continue;
1453 }
1454
1455 if (strcmp(id, "TransitionSequence") == 0) {
1456 err = parse_transition(uc_mgr, &modifier->transition_list, n);
1457 if (err < 0) {
1458 uc_error("error: failed to parse transition"
1459 " modifier");
1460 return err;
1461 }
1462 continue;
1463 }
1464
1465 if (strcmp(id, "Value") == 0) {
1466 err = parse_value(uc_mgr, &modifier->value_list, n);
1467 if (err < 0) {
1468 uc_error("error: failed to parse Value");
1469 return err;
1470 }
1471 continue;
1472 }
1473 }
1474
1475 return 0;
1476 }
1477
1478 /*
1479 * Parse Device Use Cases
1480 *
1481 * # Each device is described in new section. N devices are allowed
1482 * SectionDevice."Headphones" {
1483 * Comment "Headphones connected to 3.5mm jack"
1484 *
1485 * SupportedDevice [
1486 * "x"
1487 * "y"
1488 * ]
1489 *
1490 * ConflictingDevice [
1491 * "x"
1492 * "y"
1493 * ]
1494 *
1495 * EnableSequence [
1496 * ....
1497 * ]
1498 *
1499 * DisableSequence [
1500 * ...
1501 * ]
1502 *
1503 * TransitionSequence."ToDevice" [
1504 * ...
1505 * ]
1506 *
1507 * Value {
1508 * PlaybackVolume "name='Master Playback Volume',index=2"
1509 * PlaybackSwitch "name='Master Playback Switch',index=2"
1510 * }
1511 * }
1512 */
parse_device(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, void *data1, void *data2)1513 static int parse_device(snd_use_case_mgr_t *uc_mgr,
1514 snd_config_t *cfg,
1515 void *data1, void *data2)
1516 {
1517 struct use_case_verb *verb = data1;
1518 char *name;
1519 struct use_case_device *device;
1520 snd_config_iterator_t i, next;
1521 snd_config_t *n;
1522 int err;
1523
1524 if (parse_get_safe_name(uc_mgr, cfg, data2, &name) < 0)
1525 return -EINVAL;
1526
1527 device = calloc(1, sizeof(*device));
1528 if (device == NULL) {
1529 free(name);
1530 return -ENOMEM;
1531 }
1532 INIT_LIST_HEAD(&device->enable_list);
1533 INIT_LIST_HEAD(&device->disable_list);
1534 INIT_LIST_HEAD(&device->transition_list);
1535 INIT_LIST_HEAD(&device->dev_list.list);
1536 INIT_LIST_HEAD(&device->value_list);
1537 list_add_tail(&device->list, &verb->device_list);
1538 device->name = name;
1539
1540 /* in-place evaluation */
1541 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1542 if (err < 0)
1543 return err;
1544
1545 snd_config_for_each(i, next, cfg) {
1546 const char *id;
1547 n = snd_config_iterator_entry(i);
1548 if (snd_config_get_id(n, &id) < 0)
1549 continue;
1550
1551 if (strcmp(id, "Comment") == 0) {
1552 err = parse_string_substitute3(uc_mgr, n, &device->comment);
1553 if (err < 0) {
1554 uc_error("error: failed to get device comment");
1555 return err;
1556 }
1557 continue;
1558 }
1559
1560 if (strcmp(id, "SupportedDevice") == 0) {
1561 err = parse_device_list(uc_mgr, &device->dev_list,
1562 DEVLIST_SUPPORTED, n);
1563 if (err < 0) {
1564 uc_error("error: failed to parse supported"
1565 " device list");
1566 return err;
1567 }
1568 }
1569
1570 if (strcmp(id, "ConflictingDevice") == 0) {
1571 err = parse_device_list(uc_mgr, &device->dev_list,
1572 DEVLIST_CONFLICTING, n);
1573 if (err < 0) {
1574 uc_error("error: failed to parse conflicting"
1575 " device list");
1576 return err;
1577 }
1578 }
1579
1580 if (strcmp(id, "EnableSequence") == 0) {
1581 uc_dbg("EnableSequence");
1582 err = parse_sequence(uc_mgr, &device->enable_list, n);
1583 if (err < 0) {
1584 uc_error("error: failed to parse device enable"
1585 " sequence");
1586 return err;
1587 }
1588 continue;
1589 }
1590
1591 if (strcmp(id, "DisableSequence") == 0) {
1592 uc_dbg("DisableSequence");
1593 err = parse_sequence(uc_mgr, &device->disable_list, n);
1594 if (err < 0) {
1595 uc_error("error: failed to parse device disable"
1596 " sequence");
1597 return err;
1598 }
1599 continue;
1600 }
1601
1602 if (strcmp(id, "TransitionSequence") == 0) {
1603 uc_dbg("TransitionSequence");
1604 err = parse_transition(uc_mgr, &device->transition_list, n);
1605 if (err < 0) {
1606 uc_error("error: failed to parse transition"
1607 " device");
1608 return err;
1609 }
1610 continue;
1611 }
1612
1613 if (strcmp(id, "Value") == 0) {
1614 err = parse_value(uc_mgr, &device->value_list, n);
1615 if (err < 0) {
1616 uc_error("error: failed to parse Value");
1617 return err;
1618 }
1619 continue;
1620 }
1621 }
1622 return 0;
1623 }
1624
1625 /*
1626 * Parse Device Rename/Delete Command
1627 *
1628 * # The devices might be renamed to allow the better conditional runtime
1629 * # evaluation. Bellow example renames Speaker1 device to Speaker and
1630 * # removes Speaker2 device.
1631 * RenameDevice."Speaker1" "Speaker"
1632 * RemoveDevice."Speaker2" "Speaker2"
1633 */
parse_dev_name_list(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, struct list_head *list)1634 static int parse_dev_name_list(snd_use_case_mgr_t *uc_mgr,
1635 snd_config_t *cfg,
1636 struct list_head *list)
1637 {
1638 snd_config_t *n;
1639 snd_config_iterator_t i, next;
1640 const char *id, *name1;
1641 char *name1s, *name2;
1642 struct ucm_dev_name *dev;
1643 snd_config_iterator_t pos;
1644 int err;
1645
1646 if (snd_config_get_id(cfg, &id) < 0)
1647 return -EINVAL;
1648
1649 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1650 uc_error("compound type expected for %s", id);
1651 return -EINVAL;
1652 }
1653
1654 snd_config_for_each(i, next, cfg) {
1655 n = snd_config_iterator_entry(i);
1656
1657 if (snd_config_get_id(n, &name1) < 0)
1658 return -EINVAL;
1659
1660 err = get_string3(uc_mgr, name1, &name1s);
1661 if (err < 0)
1662 return err;
1663
1664 err = parse_string_substitute3(uc_mgr, n, &name2);
1665 if (err < 0) {
1666 free(name1s);
1667 uc_error("error: failed to get target device name for '%s'", name1);
1668 return err;
1669 }
1670
1671 /* skip duplicates */
1672 list_for_each(pos, list) {
1673 dev = list_entry(pos, struct ucm_dev_name, list);
1674 if (strcmp(dev->name1, name1s) == 0) {
1675 free(name2);
1676 free(name1s);
1677 return 0;
1678 }
1679 }
1680
1681 free(name1s);
1682
1683 dev = calloc(1, sizeof(*dev));
1684 if (dev == NULL) {
1685 free(name2);
1686 return -ENOMEM;
1687 }
1688 dev->name1 = strdup(name1);
1689 if (dev->name1 == NULL) {
1690 free(dev);
1691 free(name2);
1692 return -ENOMEM;
1693 }
1694 dev->name2 = name2;
1695 list_add_tail(&dev->list, list);
1696 }
1697
1698 return 0;
1699 }
1700
parse_compound_check_legacy(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *), void *data1)1701 static int parse_compound_check_legacy(snd_use_case_mgr_t *uc_mgr,
1702 snd_config_t *cfg,
1703 int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
1704 void *data1)
1705 {
1706 const char *id, *idchild;
1707 int child_ctr = 0, legacy_format = 1;
1708 snd_config_iterator_t i, next;
1709 snd_config_t *child;
1710 int err;
1711
1712 err = snd_config_get_id(cfg, &id);
1713 if (err < 0)
1714 return err;
1715
1716 snd_config_for_each(i, next, cfg) {
1717 child_ctr++;
1718 if (child_ctr > 1) {
1719 break;
1720 }
1721
1722 child = snd_config_iterator_entry(i);
1723
1724 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1725 legacy_format = 0;
1726 break;
1727 }
1728
1729 if (snd_config_get_id(child, &idchild) < 0)
1730 return -EINVAL;
1731
1732 if (strcmp(idchild, "0")) {
1733 legacy_format = 0;
1734 break;
1735 }
1736 }
1737 if (child_ctr != 1) {
1738 legacy_format = 0;
1739 }
1740
1741 if (legacy_format)
1742 return parse_compound(uc_mgr, cfg, fcn, data1, (void *)id);
1743 else
1744 return fcn(uc_mgr, cfg, data1, NULL);
1745 }
1746
parse_device_name(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, void *data1, void *data2 ATTRIBUTE_UNUSED)1747 static int parse_device_name(snd_use_case_mgr_t *uc_mgr,
1748 snd_config_t *cfg,
1749 void *data1,
1750 void *data2 ATTRIBUTE_UNUSED)
1751 {
1752 return parse_compound_check_legacy(uc_mgr, cfg, parse_device, data1);
1753 }
1754
parse_modifier_name(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, void *data1, void *data2 ATTRIBUTE_UNUSED)1755 static int parse_modifier_name(snd_use_case_mgr_t *uc_mgr,
1756 snd_config_t *cfg,
1757 void *data1,
1758 void *data2 ATTRIBUTE_UNUSED)
1759 {
1760 return parse_compound_check_legacy(uc_mgr, cfg, parse_modifier, data1);
1761 }
1762
verb_dev_list_add(struct use_case_verb *verb, enum dev_list_type dst_type, const char *dst, const char *src)1763 static int verb_dev_list_add(struct use_case_verb *verb,
1764 enum dev_list_type dst_type,
1765 const char *dst,
1766 const char *src)
1767 {
1768 struct use_case_device *device;
1769 struct list_head *pos;
1770
1771 list_for_each(pos, &verb->device_list) {
1772 device = list_entry(pos, struct use_case_device, list);
1773 if (strcmp(device->name, dst) != 0)
1774 continue;
1775 if (device->dev_list.type != dst_type) {
1776 if (list_empty(&device->dev_list.list)) {
1777 device->dev_list.type = dst_type;
1778 } else {
1779 uc_error("error: incompatible device list type ('%s', '%s')",
1780 device->name, src);
1781 return -EINVAL;
1782 }
1783 }
1784 return uc_mgr_put_to_dev_list(&device->dev_list, src);
1785 }
1786 uc_error("error: unable to find device '%s'", dst);
1787 return -ENOENT;
1788 }
1789
verb_dev_list_check(struct use_case_verb *verb)1790 static int verb_dev_list_check(struct use_case_verb *verb)
1791 {
1792 struct list_head *pos, *pos2;
1793 struct use_case_device *device;
1794 struct dev_list_node *dlist;
1795 int err;
1796
1797 list_for_each(pos, &verb->device_list) {
1798 device = list_entry(pos, struct use_case_device, list);
1799 list_for_each(pos2, &device->dev_list.list) {
1800 dlist = list_entry(pos2, struct dev_list_node, list);
1801 err = verb_dev_list_add(verb, device->dev_list.type,
1802 dlist->name, device->name);
1803 if (err < 0)
1804 return err;
1805 }
1806 }
1807 return 0;
1808 }
1809
verb_device_management(struct use_case_verb *verb)1810 static int verb_device_management(struct use_case_verb *verb)
1811 {
1812 struct list_head *pos;
1813 struct ucm_dev_name *dev;
1814 int err;
1815
1816 /* rename devices */
1817 list_for_each(pos, &verb->rename_list) {
1818 dev = list_entry(pos, struct ucm_dev_name, list);
1819 err = uc_mgr_rename_device(verb, dev->name1, dev->name2);
1820 if (err < 0) {
1821 uc_error("error: cannot rename device '%s' to '%s'", dev->name1, dev->name2);
1822 return err;
1823 }
1824 }
1825
1826 /* remove devices */
1827 list_for_each(pos, &verb->remove_list) {
1828 dev = list_entry(pos, struct ucm_dev_name, list);
1829 err = uc_mgr_remove_device(verb, dev->name2);
1830 if (err < 0) {
1831 uc_error("error: cannot remove device '%s'", dev->name2);
1832 return err;
1833 }
1834 }
1835
1836 /* those lists are no longer used */
1837 uc_mgr_free_dev_name_list(&verb->rename_list);
1838 uc_mgr_free_dev_name_list(&verb->remove_list);
1839
1840 /* handle conflicting/supported lists */
1841 return verb_dev_list_check(verb);
1842 }
1843
1844 /*
1845 * Parse Verb Section
1846 *
1847 * # Example Use case verb section for Voice call blah
1848 * # By Joe Blogs <joe@blogs.com>
1849 *
1850 * SectionVerb {
1851 * # enable and disable sequences are compulsory
1852 * EnableSequence [
1853 * cset "name='Master Playback Switch',index=2 0,0"
1854 * cset "name='Master Playback Volume',index=2 25,25"
1855 * msleep 50
1856 * cset "name='Master Playback Switch',index=2 1,1"
1857 * cset "name='Master Playback Volume',index=2 50,50"
1858 * ]
1859 *
1860 * DisableSequence [
1861 * cset "name='Master Playback Switch',index=2 0,0"
1862 * cset "name='Master Playback Volume',index=2 25,25"
1863 * msleep 50
1864 * cset "name='Master Playback Switch',index=2 1,1"
1865 * cset "name='Master Playback Volume',index=2 50,50"
1866 * ]
1867 *
1868 * # Optional transition verb
1869 * TransitionSequence."ToCaseName" [
1870 * msleep 1
1871 * ]
1872 *
1873 * # Optional TQ and ALSA PCMs
1874 * Value {
1875 * TQ HiFi
1876 * CapturePCM "hw:0"
1877 * PlaybackPCM "hw:0"
1878 * }
1879 * }
1880 */
parse_verb(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb, snd_config_t *cfg)1881 static int parse_verb(snd_use_case_mgr_t *uc_mgr,
1882 struct use_case_verb *verb,
1883 snd_config_t *cfg)
1884 {
1885 snd_config_iterator_t i, next;
1886 snd_config_t *n;
1887 int err;
1888
1889 /* in-place evaluation */
1890 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1891 if (err < 0)
1892 return err;
1893
1894 /* parse verb section */
1895 snd_config_for_each(i, next, cfg) {
1896 const char *id;
1897 n = snd_config_iterator_entry(i);
1898 if (snd_config_get_id(n, &id) < 0)
1899 continue;
1900
1901 if (strcmp(id, "EnableSequence") == 0) {
1902 uc_dbg("Parse EnableSequence");
1903 err = parse_sequence(uc_mgr, &verb->enable_list, n);
1904 if (err < 0) {
1905 uc_error("error: failed to parse verb enable sequence");
1906 return err;
1907 }
1908 continue;
1909 }
1910
1911 if (strcmp(id, "DisableSequence") == 0) {
1912 uc_dbg("Parse DisableSequence");
1913 err = parse_sequence(uc_mgr, &verb->disable_list, n);
1914 if (err < 0) {
1915 uc_error("error: failed to parse verb disable sequence");
1916 return err;
1917 }
1918 continue;
1919 }
1920
1921 if (strcmp(id, "TransitionSequence") == 0) {
1922 uc_dbg("Parse TransitionSequence");
1923 err = parse_transition(uc_mgr, &verb->transition_list, n);
1924 if (err < 0) {
1925 uc_error("error: failed to parse transition sequence");
1926 return err;
1927 }
1928 continue;
1929 }
1930
1931 if (strcmp(id, "Value") == 0) {
1932 uc_dbg("Parse Value");
1933 err = parse_value(uc_mgr, &verb->value_list, n);
1934 if (err < 0)
1935 return err;
1936 continue;
1937 }
1938 }
1939
1940 return 0;
1941 }
1942
1943 /*
1944 * Parse a Use case verb file.
1945 *
1946 * This file contains the following :-
1947 * o Verb enable and disable sequences.
1948 * o Supported Device enable and disable sequences for verb.
1949 * o Supported Modifier enable and disable sequences for verb
1950 * o Optional QoS for the verb and modifiers.
1951 * o Optional PCM device ID for verb and modifiers
1952 * o Alias kcontrols IDs for master and volumes and mutes.
1953 */
parse_verb_file(snd_use_case_mgr_t *uc_mgr, const char *use_case_name, const char *comment, const char *file)1954 static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
1955 const char *use_case_name,
1956 const char *comment,
1957 const char *file)
1958 {
1959 snd_config_iterator_t i, next;
1960 snd_config_t *n;
1961 struct use_case_verb *verb;
1962 snd_config_t *cfg;
1963 int err;
1964
1965 /* allocate verb */
1966 verb = calloc(1, sizeof(struct use_case_verb));
1967 if (verb == NULL)
1968 return -ENOMEM;
1969 INIT_LIST_HEAD(&verb->enable_list);
1970 INIT_LIST_HEAD(&verb->disable_list);
1971 INIT_LIST_HEAD(&verb->transition_list);
1972 INIT_LIST_HEAD(&verb->device_list);
1973 INIT_LIST_HEAD(&verb->cmpt_device_list);
1974 INIT_LIST_HEAD(&verb->modifier_list);
1975 INIT_LIST_HEAD(&verb->value_list);
1976 INIT_LIST_HEAD(&verb->rename_list);
1977 INIT_LIST_HEAD(&verb->remove_list);
1978 list_add_tail(&verb->list, &uc_mgr->verb_list);
1979 if (use_case_name == NULL)
1980 return -EINVAL;
1981 verb->name = strdup(use_case_name);
1982 if (verb->name == NULL)
1983 return -ENOMEM;
1984
1985 if (comment != NULL) {
1986 verb->comment = strdup(comment);
1987 if (verb->comment == NULL)
1988 return -ENOMEM;
1989 }
1990
1991 /* open Verb file for reading */
1992 err = uc_mgr_config_load_file(uc_mgr, file, &cfg);
1993 if (err < 0)
1994 return err;
1995
1996 /* in-place evaluation */
1997 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
1998 if (err < 0)
1999 goto _err;
2000
2001 /* parse master config sections */
2002 snd_config_for_each(i, next, cfg) {
2003 const char *id;
2004 n = snd_config_iterator_entry(i);
2005 if (snd_config_get_id(n, &id) < 0)
2006 continue;
2007
2008 /* find verb section and parse it */
2009 if (strcmp(id, "SectionVerb") == 0) {
2010 err = parse_verb(uc_mgr, verb, n);
2011 if (err < 0) {
2012 uc_error("error: %s failed to parse verb",
2013 file);
2014 goto _err;
2015 }
2016 continue;
2017 }
2018
2019 /* find device sections and parse them */
2020 if (strcmp(id, "SectionDevice") == 0) {
2021 err = parse_compound(uc_mgr, n,
2022 parse_device_name, verb, NULL);
2023 if (err < 0) {
2024 uc_error("error: %s failed to parse device",
2025 file);
2026 goto _err;
2027 }
2028 continue;
2029 }
2030
2031 /* find modifier sections and parse them */
2032 if (strcmp(id, "SectionModifier") == 0) {
2033 err = parse_compound(uc_mgr, n,
2034 parse_modifier_name, verb, NULL);
2035 if (err < 0) {
2036 uc_error("error: %s failed to parse modifier",
2037 file);
2038 goto _err;
2039 }
2040 continue;
2041 }
2042
2043 /* device renames */
2044 if (strcmp(id, "RenameDevice") == 0) {
2045 err = parse_dev_name_list(uc_mgr, n, &verb->rename_list);
2046 if (err < 0) {
2047 uc_error("error: %s failed to parse device rename",
2048 file);
2049 goto _err;
2050 }
2051 continue;
2052 }
2053
2054 /* device remove */
2055 if (strcmp(id, "RemoveDevice") == 0) {
2056 err = parse_dev_name_list(uc_mgr, n, &verb->remove_list);
2057 if (err < 0) {
2058 uc_error("error: %s failed to parse device remove",
2059 file);
2060 goto _err;
2061 }
2062 continue;
2063 }
2064
2065 /* alsa-lib configuration */
2066 if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) {
2067 err = parse_libconfig(uc_mgr, n);
2068 if (err < 0) {
2069 uc_error("error: failed to parse LibConfig");
2070 goto _err;
2071 }
2072 continue;
2073 }
2074 }
2075
2076 snd_config_delete(cfg);
2077
2078 /* use case verb must have at least 1 device */
2079 if (list_empty(&verb->device_list)) {
2080 uc_error("error: no use case device defined", file);
2081 return -EINVAL;
2082 }
2083
2084 /* do device rename and delete */
2085 err = verb_device_management(verb);
2086 if (err < 0) {
2087 uc_error("error: device management error in verb '%s'", verb->name);
2088 return err;
2089 }
2090
2091 return 0;
2092
2093 _err:
2094 snd_config_delete(cfg);
2095 return err;
2096 }
2097
2098 /*
2099 * Parse variant information
2100 */
parse_variant(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, char **_vfile, char **_vcomment)2101 static int parse_variant(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
2102 char **_vfile, char **_vcomment)
2103 {
2104 snd_config_iterator_t i, next;
2105 snd_config_t *n;
2106 char *file = NULL, *comment = NULL;
2107 int err;
2108
2109 /* parse master config sections */
2110 snd_config_for_each(i, next, cfg) {
2111 const char *id;
2112 n = snd_config_iterator_entry(i);
2113 if (snd_config_get_id(n, &id) < 0)
2114 continue;
2115
2116 /* get use case verb file name */
2117 if (strcmp(id, "File") == 0) {
2118 if (_vfile) {
2119 err = parse_string_substitute3(uc_mgr, n, &file);
2120 if (err < 0) {
2121 uc_error("failed to get File");
2122 goto __error;
2123 }
2124 }
2125 continue;
2126 }
2127
2128 /* get optional use case comment */
2129 if (strncmp(id, "Comment", 7) == 0) {
2130 if (_vcomment) {
2131 err = parse_string_substitute3(uc_mgr, n, &comment);
2132 if (err < 0) {
2133 uc_error("error: failed to get Comment");
2134 goto __error;
2135 }
2136 }
2137 continue;
2138 }
2139
2140 uc_error("unknown field '%s' in Variant section", id);
2141 err = -EINVAL;
2142 goto __error;
2143 }
2144
2145 if (_vfile)
2146 *_vfile = file;
2147 if (_vcomment)
2148 *_vcomment = comment;
2149 return 0;
2150
2151 __error:
2152 free(file);
2153 free(comment);
2154 return err;
2155 }
2156
2157 /*
2158 * Parse master section for "Use Case" and "File" tags.
2159 */
parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, void *data1 ATTRIBUTE_UNUSED, void *data2 ATTRIBUTE_UNUSED)2160 static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
2161 void *data1 ATTRIBUTE_UNUSED,
2162 void *data2 ATTRIBUTE_UNUSED)
2163 {
2164 snd_config_iterator_t i, next;
2165 snd_config_t *n, *variant = NULL;
2166 char *use_case_name, *file = NULL, *comment = NULL;
2167 bool variant_ok = false;
2168 int err;
2169
2170 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
2171 uc_error("compound type expected for use case section");
2172 return -EINVAL;
2173 }
2174
2175 err = parse_get_safe_name(uc_mgr, cfg, NULL, &use_case_name);
2176 if (err < 0) {
2177 uc_error("unable to get name for use case section");
2178 return err;
2179 }
2180
2181 /* in-place evaluation */
2182 uc_mgr->parse_master_section = 1;
2183 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
2184 uc_mgr->parse_master_section = 0;
2185 if (err < 0)
2186 goto __error;
2187
2188 /* parse master config sections */
2189 snd_config_for_each(i, next, cfg) {
2190 const char *id;
2191 n = snd_config_iterator_entry(i);
2192 if (snd_config_get_id(n, &id) < 0)
2193 continue;
2194
2195 /* get use case verb file name */
2196 if (strcmp(id, "File") == 0) {
2197 err = parse_string_substitute3(uc_mgr, n, &file);
2198 if (err < 0) {
2199 uc_error("failed to get File");
2200 goto __error;
2201 }
2202 continue;
2203 }
2204
2205 /* get optional use case comment */
2206 if (strncmp(id, "Comment", 7) == 0) {
2207 err = parse_string_substitute3(uc_mgr, n, &comment);
2208 if (err < 0) {
2209 uc_error("error: failed to get Comment");
2210 goto __error;
2211 }
2212 continue;
2213 }
2214
2215 if (uc_mgr->conf_format >= 6 && strcmp(id, "Variant") == 0) {
2216 snd_config_iterator_t i2, next2;
2217 variant = n;
2218 snd_config_for_each(i2, next2, n) {
2219 const char *id2;
2220 snd_config_t *n2;
2221 n2 = snd_config_iterator_entry(i2);
2222 if (snd_config_get_id(n2, &id2) < 0)
2223 continue;
2224 err = uc_mgr_evaluate_inplace(uc_mgr, n2);
2225 if (err < 0)
2226 goto __error;
2227 if (strcmp(use_case_name, id2) == 0)
2228 variant_ok = true;
2229 }
2230 continue;
2231 }
2232
2233 uc_error("unknown field '%s' in SectionUseCase", id);
2234 }
2235
2236 if (variant && !variant_ok) {
2237 uc_error("error: undefined variant '%s'", use_case_name);
2238 err = -EINVAL;
2239 goto __error;
2240 }
2241
2242 if (!variant) {
2243 uc_dbg("use_case_name %s file '%s'", use_case_name, file);
2244
2245 /* do we have both use case name and file ? */
2246 if (!file) {
2247 uc_error("error: use case missing file");
2248 err = -EINVAL;
2249 goto __error;
2250 }
2251
2252 /* parse verb file */
2253 err = parse_verb_file(uc_mgr, use_case_name, comment, file);
2254 } else {
2255 /* parse variants */
2256 snd_config_for_each(i, next, variant) {
2257 char *vfile, *vcomment;
2258 const char *id;
2259 n = snd_config_iterator_entry(i);
2260 if (snd_config_get_id(n, &id) < 0)
2261 continue;
2262 if (!parse_is_name_safe(id)) {
2263 err = -EINVAL;
2264 goto __error;
2265 }
2266 err = parse_variant(uc_mgr, n, &vfile, &vcomment);
2267 if (err < 0)
2268 break;
2269 uc_mgr->parse_variant = id;
2270 err = parse_verb_file(uc_mgr, id,
2271 vcomment ? vcomment : comment,
2272 vfile ? vfile : file);
2273 uc_mgr->parse_variant = NULL;
2274 free(vfile);
2275 free(vcomment);
2276 }
2277 }
2278
2279 __error:
2280 free(use_case_name);
2281 free(file);
2282 free(comment);
2283 return err;
2284 }
2285
2286 /*
2287 * parse controls which should be run only at initial boot (forcefully)
2288 */
parse_controls_fixedboot(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)2289 static int parse_controls_fixedboot(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
2290 {
2291 int err;
2292
2293 if (!list_empty(&uc_mgr->fixedboot_list)) {
2294 uc_error("FixedBoot list is not empty");
2295 return -EINVAL;
2296 }
2297 err = parse_sequence(uc_mgr, &uc_mgr->fixedboot_list, cfg);
2298 if (err < 0) {
2299 uc_error("Unable to parse FixedBootSequence");
2300 return err;
2301 }
2302
2303 return 0;
2304 }
2305
2306 /*
2307 * parse controls which should be run only at initial boot
2308 */
parse_controls_boot(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)2309 static int parse_controls_boot(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
2310 {
2311 int err;
2312
2313 if (!list_empty(&uc_mgr->boot_list)) {
2314 uc_error("Boot list is not empty");
2315 return -EINVAL;
2316 }
2317 err = parse_sequence(uc_mgr, &uc_mgr->boot_list, cfg);
2318 if (err < 0) {
2319 uc_error("Unable to parse BootSequence");
2320 return err;
2321 }
2322
2323 return 0;
2324 }
2325
2326 /*
2327 * parse controls
2328 */
parse_controls(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)2329 static int parse_controls(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
2330 {
2331 int err;
2332
2333 if (!list_empty(&uc_mgr->default_list)) {
2334 uc_error("Default list is not empty");
2335 return -EINVAL;
2336 }
2337 err = parse_sequence(uc_mgr, &uc_mgr->default_list, cfg);
2338 if (err < 0) {
2339 uc_error("Unable to parse SectionDefaults");
2340 return err;
2341 }
2342
2343 return 0;
2344 }
2345
2346 /*
2347 * Each sound card has a master sound card file that lists all the supported
2348 * use case verbs for that sound card. i.e.
2349 *
2350 * #Example master file for blah sound card
2351 * #By Joe Blogs <joe@bloggs.org>
2352 *
2353 * Comment "Nice Abstracted Soundcard"
2354 *
2355 * # The file is divided into Use case sections. One section per use case verb.
2356 *
2357 * SectionUseCase."Voice Call" {
2358 * File "voice_call_blah"
2359 * Comment "Make a voice phone call."
2360 * }
2361 *
2362 * SectionUseCase."HiFi" {
2363 * File "hifi_blah"
2364 * Comment "Play and record HiFi quality Music."
2365 * }
2366 *
2367 * # Define Value defaults
2368 *
2369 * ValueDefaults {
2370 * PlaybackCTL "hw:CARD=0"
2371 * CaptureCTL "hw:CARD=0"
2372 * }
2373 *
2374 * # The initial boot (run once) configuration.
2375 *
2376 * BootSequence [
2377 * cset "name='Master Playback Switch',index=2 1,1"
2378 * cset "name='Master Playback Volume',index=2 25,25"
2379 * ]
2380 *
2381 * # This file also stores the default sound card state.
2382 *
2383 * SectionDefaults [
2384 * cset "name='Master Mono Playback',index=1 0"
2385 * cset "name='Master Mono Playback Volume',index=1 0"
2386 * cset "name='PCM Switch',index=2 1,1"
2387 * exec "some binary here"
2388 * msleep 50
2389 * ........
2390 * ]
2391 *
2392 * # End of example file.
2393 */
parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)2394 static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
2395 {
2396 snd_config_iterator_t i, next;
2397 snd_config_t *n;
2398 const char *id;
2399 int err;
2400
2401 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
2402 uc_error("compound type expected for master file");
2403 return -EINVAL;
2404 }
2405
2406 if (uc_mgr->conf_format >= 2) {
2407 err = parse_syntax_field(uc_mgr, cfg, uc_mgr->conf_file_name);
2408 if (err < 0)
2409 return err;
2410 }
2411
2412 /* in-place evaluation */
2413 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
2414 if (err < 0)
2415 return err;
2416
2417 /* parse master config sections */
2418 snd_config_for_each(i, next, cfg) {
2419
2420 n = snd_config_iterator_entry(i);
2421 if (snd_config_get_id(n, &id) < 0)
2422 continue;
2423
2424 if (strcmp(id, "Comment") == 0) {
2425 err = parse_string_substitute3(uc_mgr, n, &uc_mgr->comment);
2426 if (err < 0) {
2427 uc_error("error: failed to get master comment");
2428 return err;
2429 }
2430 continue;
2431 }
2432
2433 /* find use case section and parse it */
2434 if (strcmp(id, "SectionUseCase") == 0) {
2435 err = parse_compound(uc_mgr, n,
2436 parse_master_section,
2437 NULL, NULL);
2438 if (err < 0)
2439 return err;
2440 continue;
2441 }
2442
2443 /* find default control values section (force boot sequence only) */
2444 if (strcmp(id, "FixedBootSequence") == 0) {
2445 err = parse_controls_fixedboot(uc_mgr, n);
2446 if (err < 0)
2447 return err;
2448 continue;
2449 }
2450
2451 /* find default control values section (first boot only) */
2452 if (strcmp(id, "BootSequence") == 0) {
2453 err = parse_controls_boot(uc_mgr, n);
2454 if (err < 0)
2455 return err;
2456 continue;
2457 }
2458
2459 /* find default control values section and parse it */
2460 if (strcmp(id, "SectionDefaults") == 0) {
2461 err = parse_controls(uc_mgr, n);
2462 if (err < 0)
2463 return err;
2464 continue;
2465 }
2466
2467 /* get the default values */
2468 if (strcmp(id, "ValueDefaults") == 0) {
2469 err = parse_value(uc_mgr, &uc_mgr->value_list, n);
2470 if (err < 0) {
2471 uc_error("error: failed to parse ValueDefaults");
2472 return err;
2473 }
2474 continue;
2475 }
2476
2477 /* alsa-lib configuration */
2478 if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) {
2479 err = parse_libconfig(uc_mgr, n);
2480 if (err < 0) {
2481 uc_error("error: failed to parse LibraryConfig");
2482 return err;
2483 }
2484 continue;
2485 }
2486
2487 /* error */
2488 if (strcmp(id, "Error") == 0)
2489 return error_node(uc_mgr, n);
2490
2491 /* skip further Syntax value updates (Include) */
2492 if (strcmp(id, "Syntax") == 0)
2493 continue;
2494
2495 uc_error("unknown master file field %s", id);
2496 }
2497 return 0;
2498 }
2499
2500 /* get the card info */
get_card_info(snd_use_case_mgr_t *mgr, const char *ctl_name, snd_ctl_card_info_t **info)2501 static int get_card_info(snd_use_case_mgr_t *mgr,
2502 const char *ctl_name,
2503 snd_ctl_card_info_t **info)
2504 {
2505 struct ctl_list *ctl_list;
2506 int err;
2507
2508 err = uc_mgr_open_ctl(mgr, &ctl_list, ctl_name, 0);
2509 if (err < 0)
2510 return err;
2511
2512 if (info)
2513 *info = ctl_list->ctl_info;
2514 return err;
2515 }
2516
2517 /* find the card in the local machine */
get_by_card_name(snd_use_case_mgr_t *mgr, const char *card_name)2518 static int get_by_card_name(snd_use_case_mgr_t *mgr, const char *card_name)
2519 {
2520 int card, err;
2521 snd_ctl_card_info_t *info;
2522 const char *_driver, *_name, *_long_name;
2523
2524 snd_ctl_card_info_alloca(&info);
2525
2526 card = -1;
2527 if (snd_card_next(&card) < 0 || card < 0) {
2528 uc_error("no soundcards found...");
2529 return -1;
2530 }
2531
2532 while (card >= 0) {
2533 char name[32];
2534
2535 /* clear the list, keep the only one CTL device */
2536 uc_mgr_free_ctl_list(mgr);
2537
2538 sprintf(name, "hw:%d", card);
2539 err = get_card_info(mgr, name, &info);
2540
2541 if (err == 0) {
2542 _driver = snd_ctl_card_info_get_driver(info);
2543 _name = snd_ctl_card_info_get_name(info);
2544 _long_name = snd_ctl_card_info_get_longname(info);
2545 if (!strcmp(card_name, _driver) ||
2546 !strcmp(card_name, _name) ||
2547 !strcmp(card_name, _long_name))
2548 return 0;
2549 }
2550
2551 if (snd_card_next(&card) < 0) {
2552 uc_error("snd_card_next");
2553 break;
2554 }
2555 }
2556
2557 uc_mgr_free_ctl_list(mgr);
2558
2559 return -1;
2560 }
2561
2562 /* set the driver name and long name by the card ctl name */
get_by_card(snd_use_case_mgr_t *mgr, const char *ctl_name)2563 static inline int get_by_card(snd_use_case_mgr_t *mgr, const char *ctl_name)
2564 {
2565 return get_card_info(mgr, ctl_name, NULL);
2566 }
2567
parse_toplevel_path(snd_use_case_mgr_t *uc_mgr, char *filename, snd_config_t *cfg)2568 static int parse_toplevel_path(snd_use_case_mgr_t *uc_mgr,
2569 char *filename,
2570 snd_config_t *cfg)
2571 {
2572 snd_config_iterator_t i, next, i2, next2;
2573 snd_config_t *n, *n2;
2574 const char *id;
2575 char *dir = NULL, *file = NULL, fn[PATH_MAX];
2576 struct stat64 st;
2577 long version;
2578 int err;
2579
2580 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
2581 uc_error("compound type expected for UseCasePath node");
2582 return -EINVAL;
2583 }
2584
2585 /* parse use case path config sections */
2586 snd_config_for_each(i, next, cfg) {
2587 n = snd_config_iterator_entry(i);
2588
2589 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
2590 uc_error("compound type expected for UseCasePath.something node");
2591 return -EINVAL;
2592 }
2593
2594 if (snd_config_get_id(n, &id) < 0)
2595 continue;
2596
2597 version = 2;
2598
2599 /* parse use case path config sections */
2600 snd_config_for_each(i2, next2, n) {
2601
2602 n2 = snd_config_iterator_entry(i2);
2603 if (snd_config_get_id(n2, &id) < 0)
2604 continue;
2605
2606 if (strcmp(id, "Version") == 0) {
2607 err = parse_integer_substitute(uc_mgr, n2, &version);
2608 if (err < 0) {
2609 uc_error("unable to parse UcmDirectory");
2610 goto __error;
2611 }
2612 if (version < 1 || version > 2) {
2613 uc_error("Version must be 1 or 2");
2614 err = -EINVAL;
2615 goto __error;
2616 }
2617 continue;
2618 }
2619
2620 if (strcmp(id, "Directory") == 0) {
2621 err = parse_string_substitute(uc_mgr, n2, &dir);
2622 if (err < 0) {
2623 uc_error("unable to parse Directory");
2624 goto __error;
2625 }
2626 continue;
2627 }
2628
2629 if (strcmp(id, "File") == 0) {
2630 err = parse_string_substitute(uc_mgr, n2, &file);
2631 if (err < 0) {
2632 uc_error("unable to parse File");
2633 goto __error;
2634 }
2635 continue;
2636 }
2637
2638 uc_error("unknown UseCasePath field %s", id);
2639 }
2640
2641 if (dir == NULL) {
2642 uc_error("Directory is not defined in %s!", filename);
2643 goto __next;
2644 }
2645 if (file == NULL) {
2646 uc_error("File is not defined in %s!", filename);
2647 goto __next;
2648 }
2649
2650 ucm_filename(fn, sizeof(fn), version, dir, file);
2651 if (access(fn, R_OK) == 0 && lstat64(fn, &st) == 0) {
2652 if (S_ISLNK(st.st_mode)) {
2653 ssize_t r;
2654 char *link, *dir2, *p;
2655
2656 link = malloc(PATH_MAX);
2657 if (link == NULL)
2658 goto __enomem;
2659 r = readlink(fn, link, PATH_MAX - 1);
2660 if (r <= 0) {
2661 free(link);
2662 goto __next;
2663 }
2664 link[r] = '\0';
2665 p = strrchr(link, '/');
2666 if (p) {
2667 *p = '\0';
2668 dir2 = malloc(PATH_MAX);
2669 if (dir2 == NULL) {
2670 free(link);
2671 goto __enomem;
2672 }
2673 strncpy(dir2, dir, PATH_MAX - 1);
2674 strncat(dir2, "/", PATH_MAX - 1);
2675 strncat(dir2, link, PATH_MAX - 1);
2676 fn[PATH_MAX - 1] = '\0';
2677 free(dir);
2678 dir = dir2;
2679 }
2680 free(link);
2681 }
2682 if (replace_string(&uc_mgr->conf_dir_name, dir) == NULL)
2683 goto __enomem;
2684 if (replace_string(&uc_mgr->conf_file_name, file) == NULL)
2685 goto __enomem;
2686 strncpy(filename, fn, PATH_MAX);
2687 filename[PATH_MAX - 1] = '\0';
2688 uc_mgr->conf_format = version;
2689 goto __ok;
2690 }
2691
2692 __next:
2693 free(file);
2694 if (dir != fn)
2695 free(dir);
2696 dir = NULL;
2697 file = NULL;
2698 }
2699
2700 err = -ENOENT;
2701 goto __error;
2702
2703 __enomem:
2704 err = -ENOMEM;
2705 goto __error;
2706
2707 __ok:
2708 err = 0;
2709 __error:
2710 free(file);
2711 if (dir != fn)
2712 free(dir);
2713 return err;
2714 }
2715
parse_toplevel_config(snd_use_case_mgr_t *uc_mgr, char *filename, snd_config_t *cfg)2716 static int parse_toplevel_config(snd_use_case_mgr_t *uc_mgr,
2717 char *filename,
2718 snd_config_t *cfg)
2719 {
2720 snd_config_iterator_t i, next;
2721 snd_config_t *n;
2722 const char *id;
2723 int err;
2724
2725 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
2726 uc_error("compound type expected for toplevel file");
2727 return -EINVAL;
2728 }
2729
2730 err = parse_syntax_field(uc_mgr, cfg, filename);
2731 if (err < 0)
2732 return err;
2733
2734 /* in-place evaluation */
2735 err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
2736 if (err < 0)
2737 return err;
2738
2739 /* parse toplevel config sections */
2740 snd_config_for_each(i, next, cfg) {
2741
2742 n = snd_config_iterator_entry(i);
2743 if (snd_config_get_id(n, &id) < 0)
2744 continue;
2745
2746 if (strcmp(id, "UseCasePath") == 0) {
2747 err = parse_toplevel_path(uc_mgr, filename, n);
2748 if (err == 0)
2749 return err;
2750 continue;
2751 }
2752
2753 /* alsa-lib configuration */
2754 if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) {
2755 err = parse_libconfig(uc_mgr, n);
2756 if (err < 0) {
2757 uc_error("error: failed to parse LibConfig");
2758 return err;
2759 }
2760 continue;
2761 }
2762
2763 /* skip further Syntax value updates (Include) */
2764 if (strcmp(id, "Syntax") == 0)
2765 continue;
2766
2767 uc_error("unknown toplevel field %s", id);
2768 }
2769
2770 return -ENOENT;
2771 }
2772
load_toplevel_config(snd_use_case_mgr_t *uc_mgr, snd_config_t **cfg)2773 static int load_toplevel_config(snd_use_case_mgr_t *uc_mgr,
2774 snd_config_t **cfg)
2775 {
2776 char filename[PATH_MAX];
2777 snd_config_t *tcfg;
2778 int err;
2779
2780 ucm_filename(filename, sizeof(filename), 2, NULL, "ucm.conf");
2781
2782 if (access(filename, R_OK) != 0) {
2783 uc_error("Unable to find the top-level configuration file '%s'.", filename);
2784 return -ENOENT;
2785 }
2786
2787 err = uc_mgr_config_load(2, filename, &tcfg);
2788 if (err < 0)
2789 goto __error;
2790
2791 /* filename is shared for function input and output! */
2792 err = parse_toplevel_config(uc_mgr, filename, tcfg);
2793 snd_config_delete(tcfg);
2794 if (err < 0)
2795 goto __error;
2796
2797 err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg);
2798 if (err < 0) {
2799 uc_error("error: could not parse configuration for card %s",
2800 uc_mgr->card_name);
2801 goto __error;
2802 }
2803
2804 return 0;
2805
2806 __error:
2807 return err;
2808 }
2809
2810 /* load master use case file for sound card based on rules in ucm2/ucm.conf
2811 */
uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)2812 int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
2813 {
2814 snd_config_t *cfg;
2815 const char *name;
2816 int err;
2817
2818 err = snd_config_top(&uc_mgr->local_config);
2819 if (err < 0)
2820 return err;
2821
2822 err = snd_config_top(&uc_mgr->macros);
2823 if (err < 0)
2824 return err;
2825
2826 name = uc_mgr->card_name;
2827 if (strncmp(name, "hw:", 3) == 0) {
2828 err = get_by_card(uc_mgr, name);
2829 if (err < 0) {
2830 uc_error("card '%s' is not valid", name);
2831 goto __error;
2832 }
2833 } else if (strncmp(name, "strict:", 7)) {
2834 /* do not handle the error here */
2835 /* we can refer the virtual UCM config */
2836 get_by_card_name(uc_mgr, name);
2837 }
2838
2839 err = load_toplevel_config(uc_mgr, &cfg);
2840 if (err < 0)
2841 goto __error;
2842
2843 err = parse_master_file(uc_mgr, cfg);
2844 if (uc_mgr->macros) {
2845 snd_config_delete(uc_mgr->macros);
2846 uc_mgr->macros = NULL;
2847 }
2848 snd_config_delete(cfg);
2849 if (err < 0) {
2850 uc_mgr_free_ctl_list(uc_mgr);
2851 uc_mgr_free_verb(uc_mgr);
2852 }
2853
2854 return err;
2855
2856 __error:
2857 uc_mgr_free_ctl_list(uc_mgr);
2858 replace_string(&uc_mgr->conf_dir_name, NULL);
2859 return err;
2860 }
2861
filename_filter(const struct dirent64 *dirent)2862 static int filename_filter(const struct dirent64 *dirent)
2863 {
2864 if (dirent == NULL)
2865 return 0;
2866 if (dirent->d_type == DT_DIR) {
2867 if (dirent->d_name[0] == '.') {
2868 if (dirent->d_name[1] == '\0')
2869 return 0;
2870 if (dirent->d_name[1] == '.' &&
2871 dirent->d_name[2] == '\0')
2872 return 0;
2873 }
2874 return 1;
2875 }
2876 return 0;
2877 }
2878
2879 /* scan all cards and comments
2880 *
2881 * Cards are defined by machines. Each card/machine installs its UCM
2882 * configuration files in a subdirectory with the same name as the sound
2883 * card under /usr/share/alsa/ucm2. This function will scan all the card
2884 * directories and skip the component directories defined in the array
2885 * component_dir.
2886 */
uc_mgr_scan_master_configs(const char **_list[])2887 int uc_mgr_scan_master_configs(const char **_list[])
2888 {
2889 char filename[PATH_MAX], dfl[PATH_MAX], fn[FILENAME_MAX];
2890 char *env = getenv(ALSA_CONFIG_UCM2_VAR);
2891 snd_use_case_mgr_t *uc_mgr;
2892 const char **list, *d_name;
2893 char *s;
2894 snd_config_t *cfg, *c;
2895 int i, j, cnt, err, cards;
2896 long l;
2897 ssize_t ss;
2898 struct dirent64 **namelist;
2899
2900 i = -1;
2901 cards = 0;
2902 while (1) {
2903 err = snd_card_next(&i);
2904 if (err < 0)
2905 return err;
2906 if (i < 0)
2907 break;
2908 cards++;
2909 }
2910 cards += 4; /* plug-and-play */
2911
2912 if (env)
2913 snprintf(filename, sizeof(filename), "%s/conf.virt.d", env);
2914 else
2915 snprintf(filename, sizeof(filename), "%s/ucm2/conf.virt.d",
2916 snd_config_topdir());
2917
2918 #if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__sun) && !defined(__ANDROID__)
2919 #define SORTFUNC versionsort64
2920 #else
2921 #define SORTFUNC alphasort64
2922 #endif
2923 err = scandir64(filename, &namelist, filename_filter, SORTFUNC);
2924 if (err < 0) {
2925 err = -errno;
2926 uc_error("error: could not scan directory %s: %s",
2927 filename, strerror(-err));
2928 return err;
2929 }
2930 cnt = err;
2931
2932 dfl[0] = '\0';
2933 if (strlen(filename) + 8 < sizeof(filename)) {
2934 strcat(filename, "/default");
2935 ss = readlink(filename, dfl, sizeof(dfl)-1);
2936 if (ss >= 0) {
2937 dfl[ss] = '\0';
2938 dfl[sizeof(dfl)-1] = '\0';
2939 if (dfl[0] && dfl[strlen(dfl)-1] == '/')
2940 dfl[strlen(dfl)-1] = '\0';
2941 } else {
2942 dfl[0] = '\0';
2943 }
2944 }
2945
2946 j = 0;
2947 list = calloc(1, (cards + cnt) * 2 * sizeof(char *));
2948 if (list == NULL) {
2949 err = -ENOMEM;
2950 goto __err;
2951 }
2952
2953 i = -1;
2954 while (j / 2 < cards) {
2955 err = snd_card_next(&i);
2956 if (err < 0)
2957 goto __err;
2958 if (i < 0)
2959 break;
2960 snprintf(fn, sizeof(fn), "-hw:%d", i);
2961 err = snd_use_case_mgr_open(&uc_mgr, fn);
2962 if (err == -ENOENT || err == -ENXIO)
2963 continue;
2964 if (err < 0) {
2965 uc_error("Unable to open '%s': %s", fn, snd_strerror(err));
2966 goto __err;
2967 }
2968 err = snd_use_case_get(uc_mgr, "comment", (const char **)&s);
2969 if (err < 0) {
2970 err = snd_card_get_longname(i, &s);
2971 if (err < 0)
2972 goto __err;
2973 }
2974 snd_use_case_mgr_close(uc_mgr);
2975 list[j] = strdup(fn + 1);
2976 if (list[j] == NULL) {
2977 free(s);
2978 err = -ENOMEM;
2979 goto __err;
2980 }
2981 list[j + 1] = s;
2982 j += 2;
2983 }
2984
2985 for (i = 0; i < cnt; i++) {
2986
2987 d_name = namelist[i]->d_name;
2988
2989 snprintf(fn, sizeof(fn), "%s.conf", d_name);
2990 ucm_filename(filename, sizeof(filename), 2, d_name, fn);
2991 #ifdef HAVE_EACCESS
2992 if (eaccess(filename, R_OK))
2993 #else
2994 if (access(filename, R_OK))
2995 #endif
2996 continue;
2997
2998 err = uc_mgr_config_load(2, filename, &cfg);
2999 if (err < 0)
3000 goto __err;
3001 err = snd_config_search(cfg, "Syntax", &c);
3002 if (err < 0) {
3003 uc_error("Syntax field not found in %s", d_name);
3004 snd_config_delete(cfg);
3005 continue;
3006 }
3007 err = snd_config_get_integer(c, &l);
3008 if (err < 0) {
3009 uc_error("Syntax field is invalid in %s", d_name);
3010 snd_config_delete(cfg);
3011 goto __err;
3012 }
3013 if (l < 2 || l > SYNTAX_VERSION_MAX) {
3014 uc_error("Incompatible syntax %d in %s", l, d_name);
3015 snd_config_delete(cfg);
3016 goto __err;
3017 }
3018 err = snd_config_search(cfg, "Comment", &c);
3019 if (err >= 0) {
3020 err = parse_string(c, (char **)&list[j+1]);
3021 if (err < 0) {
3022 snd_config_delete(cfg);
3023 goto __err;
3024 }
3025 }
3026 snd_config_delete(cfg);
3027 list[j] = strdup(d_name);
3028 if (list[j] == NULL) {
3029 err = -ENOMEM;
3030 goto __err;
3031 }
3032 if (strcmp(dfl, list[j]) == 0) {
3033 /* default to top */
3034 const char *save1 = list[j];
3035 const char *save2 = list[j + 1];
3036 memmove(list + 2, list, j * sizeof(char *));
3037 list[0] = save1;
3038 list[1] = save2;
3039 }
3040 j += 2;
3041 }
3042 err = 0;
3043
3044 __err:
3045 for (i = 0; i < cnt; i++)
3046 free(namelist[i]);
3047 free(namelist);
3048 if (err < 0) {
3049 for (i = 0; i < j; i++) {
3050 free((void *)list[i * 2]);
3051 free((void *)list[i * 2 + 1]);
3052 }
3053 free(list);
3054 return err;
3055 }
3056
3057 *_list = list;
3058 return j;
3059 }
3060