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