1 /*
2 Copyright(c) 2021 Intel Corporation
3 All rights reserved.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of version 2 of the GNU General Public License as
7 published by the Free Software Foundation.
8
9 This program is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 The full GNU General Public License is included in this distribution
18 in the file called LICENSE.GPL.
19 */
20
21 #include "aconfig.h"
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <stddef.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <regex.h>
29 #include <dlfcn.h>
30
31 #include <alsa/asoundlib.h>
32 #include "gettext.h"
33 #include "topology.h"
34 #include "pre-processor.h"
35 #include "pre-process-external.h"
36
37 #define SND_TOPOLOGY_MAX_PLUGINS 32
38
get_plugin_string(struct tplg_pre_processor *tplg_pp, char **plugin_string)39 static int get_plugin_string(struct tplg_pre_processor *tplg_pp, char **plugin_string)
40 {
41 const char *lib_names_t = NULL;
42 snd_config_t *defines;
43 int ret;
44
45 ret = snd_config_search(tplg_pp->input_cfg, "Define.PREPROCESS_PLUGINS", &defines);
46 if (ret < 0)
47 return ret;
48
49 ret = snd_config_get_string(defines, &lib_names_t);
50 if (ret < 0)
51 return ret;
52
53 *plugin_string = strdup(lib_names_t);
54
55 if (!*plugin_string)
56 return -ENOMEM;
57
58 return 0;
59 }
60
run_plugin(struct tplg_pre_processor *tplg_pp, char *plugin)61 static int run_plugin(struct tplg_pre_processor *tplg_pp, char *plugin)
62 {
63 plugin_pre_process process;
64 char *xlib, *xfunc, *path;
65 void *h = NULL;
66 int ret = 0;
67
68 /* compose the plugin path, if not from environment, then from default plugins dir */
69 path = getenv("ALSA_TOPOLOGY_PLUGIN_DIR");
70 if (!path)
71 path = ALSA_TOPOLOGY_PLUGIN_DIR;
72
73 xlib = tplg_snprintf("%s/%s%s%s", path, PROCESS_LIB_PREFIX, plugin,
74 PROCESS_LIB_POSTFIX);
75 xfunc = tplg_snprintf("%s%s%s", PROCESS_FUNC_PREFIX, plugin,
76 PROCESS_FUNC_POSTFIX);
77
78 if (!xlib || !xfunc) {
79 fprintf(stderr, "can't reserve memory for plugin paths and func names\n");
80 ret = -ENOMEM;
81 goto err;
82 }
83
84 /* open plugin */
85 h = dlopen(xlib, RTLD_NOW);
86 if (!h) {
87 fprintf(stderr, "unable to open library '%s'\n", xlib);
88 ret = -EINVAL;
89 goto err;
90 }
91
92 /* find function */
93 process = dlsym(h, xfunc);
94
95 if (!process) {
96 fprintf(stderr, "symbol 'topology_process' was not found in %s\n", xlib);
97 ret = -EINVAL;
98 goto err;
99 }
100
101 /* process plugin */
102 ret = process(tplg_pp->input_cfg, tplg_pp->output_cfg);
103
104 err:
105 if (h)
106 dlclose(h);
107 if (xlib)
108 free(xlib);
109 if (xfunc)
110 free(xfunc);
111
112 return ret;
113 }
114
pre_process_plugins(struct tplg_pre_processor *tplg_pp)115 static int pre_process_plugins(struct tplg_pre_processor *tplg_pp)
116 {
117 char *plugins[SND_TOPOLOGY_MAX_PLUGINS];
118 char *plugin_string;
119 int count;
120 int ret;
121 int i;
122
123 /* parse plugin names */
124 ret = get_plugin_string(tplg_pp, &plugin_string);
125
126 /* no plugins defined, so just return */
127 if (ret < 0)
128 return 0;
129
130 count = 0;
131 plugins[count] = strtok(plugin_string, ":");
132 while ((count < SND_TOPOLOGY_MAX_PLUGINS - 1) && plugins[count]) {
133 count++;
134 plugins[count] = strtok(NULL, ":");
135 }
136
137 /* run all plugins */
138 for (i = 0; i < count; i++) {
139 ret = run_plugin(tplg_pp, plugins[i]);
140 if (ret < 0)
141 return ret;
142 }
143
144 free(plugin_string);
145
146 return 0;
147 }
148
149 /*
150 * Helper function to find config by id.
151 * Topology2.0 object names are constructed with attribute values separated by '.'.
152 * So snd_config_search() cannot be used as it interprets the '.' as the node separator.
153 */
tplg_find_config(snd_config_t *config, const char *name)154 snd_config_t *tplg_find_config(snd_config_t *config, const char *name)
155 {
156 snd_config_iterator_t i, next;
157 snd_config_t *n;
158 const char *id;
159
160 snd_config_for_each(i, next, config) {
161 n = snd_config_iterator_entry(i);
162 if (snd_config_get_id(n, &id) < 0)
163 continue;
164
165 if (!strcmp(id, name))
166 return n;
167 }
168
169 return NULL;
170 }
171
172 /* make a new config and add it to parent */
tplg_config_make_add(snd_config_t **config, const char *id, snd_config_type_t type, snd_config_t *parent)173 int tplg_config_make_add(snd_config_t **config, const char *id, snd_config_type_t type,
174 snd_config_t *parent)
175 {
176 int ret;
177
178 ret = snd_config_make(config, id, type);
179 if (ret < 0)
180 return ret;
181
182 ret = snd_config_add(parent, *config);
183 if (ret < 0)
184 snd_config_delete(*config);
185
186 return ret;
187 }
188
189 /*
190 * The pre-processor will need to concat multiple strings separate by '.' to construct the object
191 * name and search for configs with ID's separated by '.'.
192 * This function helps concat input strings in the specified input format
193 */
tplg_snprintf(char *fmt, ...)194 char *tplg_snprintf(char *fmt, ...)
195 {
196 char *string;
197 int len = 1;
198
199 va_list va;
200
201 va_start(va, fmt);
202 len += vsnprintf(NULL, 0, fmt, va);
203 va_end(va);
204
205 string = calloc(1, len);
206 if (!string)
207 return NULL;
208
209 va_start(va, fmt);
210 vsnprintf(string, len, fmt, va);
211 va_end(va);
212
213 return string;
214 }
215
216 #ifdef TPLG_DEBUG
tplg_pp_debug(char *fmt, ...)217 void tplg_pp_debug(char *fmt, ...)
218 {
219 char msg[DEBUG_MAX_LENGTH];
220 va_list va;
221
222 va_start(va, fmt);
223 vsnprintf(msg, DEBUG_MAX_LENGTH, fmt, va);
224 va_end(va);
225
226 fprintf(stdout, "%s\n", msg);
227 }
228
tplg_pp_config_debug(struct tplg_pre_processor *tplg_pp, snd_config_t *cfg)229 void tplg_pp_config_debug(struct tplg_pre_processor *tplg_pp, snd_config_t *cfg)
230 {
231 snd_config_save(cfg, tplg_pp->dbg_output);
232 }
233 #else
tplg_pp_debug(char *fmt ATTRIBUTE_UNUSED, ...)234 void tplg_pp_debug(char *fmt ATTRIBUTE_UNUSED, ...) {}
tplg_pp_config_debug(struct tplg_pre_processor *tplg_pp ATTRIBUTE_UNUSED, snd_config_t *cfg ATTRIBUTE_UNUSED)235 void tplg_pp_config_debug(struct tplg_pre_processor *tplg_pp ATTRIBUTE_UNUSED,
236 snd_config_t *cfg ATTRIBUTE_UNUSED) {}
237 #endif
238
pre_process_config(struct tplg_pre_processor *tplg_pp, snd_config_t *cfg)239 static int pre_process_config(struct tplg_pre_processor *tplg_pp, snd_config_t *cfg)
240 {
241 snd_config_iterator_t i, next, i2, next2;
242 snd_config_t *n, *n2;
243 const char *id;
244 int err;
245
246 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
247 fprintf(stderr, "compound type expected at top level");
248 return -EINVAL;
249 }
250
251 /* parse topology objects */
252 snd_config_for_each(i, next, cfg) {
253 n = snd_config_iterator_entry(i);
254 if (snd_config_get_id(n, &id) < 0)
255 continue;
256
257 if (strcmp(id, "Object"))
258 continue;
259
260 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
261 fprintf(stderr, "compound type expected for %s", id);
262 return -EINVAL;
263 }
264
265 snd_config_for_each(i2, next2, n) {
266 n2 = snd_config_iterator_entry(i2);
267
268 if (snd_config_get_id(n2, &id) < 0)
269 continue;
270
271 if (snd_config_get_type(n2) != SND_CONFIG_TYPE_COMPOUND) {
272 fprintf(stderr, "compound type expected for %s", id);
273 return -EINVAL;
274 }
275
276 /* pre-process Object instance. Top-level object have no parent */
277 err = tplg_pre_process_objects(tplg_pp, n2, NULL);
278 if (err < 0)
279 return err;
280 }
281 }
282
283 return 0;
284 }
285
free_pre_processor(struct tplg_pre_processor *tplg_pp)286 void free_pre_processor(struct tplg_pre_processor *tplg_pp)
287 {
288 snd_output_close(tplg_pp->output);
289 snd_output_close(tplg_pp->dbg_output);
290 snd_config_delete(tplg_pp->output_cfg);
291 if (tplg_pp->define_cfg)
292 snd_config_delete(tplg_pp->define_cfg);
293 free(tplg_pp->inc_path);
294 free(tplg_pp);
295 }
296
init_pre_processor(struct tplg_pre_processor **tplg_pp, snd_output_type_t type, const char *output_file)297 int init_pre_processor(struct tplg_pre_processor **tplg_pp, snd_output_type_t type,
298 const char *output_file)
299 {
300 struct tplg_pre_processor *_tplg_pp;
301 int ret;
302
303 _tplg_pp = calloc(1, sizeof(struct tplg_pre_processor));
304 if (!_tplg_pp)
305 return -ENOMEM;
306
307 *tplg_pp = _tplg_pp;
308
309 /* create output top-level config node */
310 ret = snd_config_top(&_tplg_pp->output_cfg);
311 if (ret < 0)
312 goto err;
313
314 /* open output based on type */
315 if (type == SND_OUTPUT_STDIO) {
316 ret = snd_output_stdio_open(&_tplg_pp->output, output_file, "w");
317 if (ret < 0) {
318 fprintf(stderr, "failed to open file output\n");
319 goto open_err;
320 }
321 } else {
322 ret = snd_output_buffer_open(&_tplg_pp->output);
323 if (ret < 0) {
324 fprintf(stderr, "failed to open buffer output\n");
325 goto open_err;
326 }
327 }
328
329 /* debug output */
330 ret = snd_output_stdio_attach(&_tplg_pp->dbg_output, stdout, 0);
331 if (ret < 0) {
332 fprintf(stderr, "failed to open stdout output\n");
333 goto out_close;
334 }
335
336 return 0;
337 out_close:
338 snd_output_close(_tplg_pp->output);
339 open_err:
340 snd_config_delete(_tplg_pp->output_cfg);
341 err:
342 free(_tplg_pp);
343 return ret;
344 }
345
346 #if SND_LIB_VER(1, 2, 5) < SND_LIB_VERSION
pre_process_set_defines(struct tplg_pre_processor *tplg_pp, const char *pre_processor_defs)347 static int pre_process_set_defines(struct tplg_pre_processor *tplg_pp, const char *pre_processor_defs)
348 {
349 int ret;
350
351 /*
352 * load the command line defines to the configuration tree
353 */
354 if (pre_processor_defs != NULL) {
355 ret = snd_config_load_string(&tplg_pp->define_cfg, pre_processor_defs, 0);
356 if (ret < 0) {
357 fprintf(stderr, "Failed to load pre-processor command line definitions\n");
358 return ret;
359 }
360 } else {
361 tplg_pp->define_cfg = NULL;
362 }
363
364 return 0;
365 }
366
pre_process_add_defines(struct tplg_pre_processor *tplg_pp, snd_config_t *from)367 static int pre_process_add_defines(struct tplg_pre_processor *tplg_pp, snd_config_t *from)
368 {
369 snd_config_t *conf_defines, *conf_tmp;
370 int ret;
371
372 ret = snd_config_search(from, "Define", &conf_defines);
373 if (ret == -ENOENT) {
374 if (tplg_pp->input_cfg == from && tplg_pp->define_cfg) {
375 conf_defines = NULL;
376 goto create;
377 }
378 }
379 if (ret < 0)
380 return ret;
381
382 if (snd_config_get_type(conf_defines) != SND_CONFIG_TYPE_COMPOUND) {
383 fprintf(stderr, "Define must be a compound!\n");
384 return -EINVAL;
385 }
386
387 if (tplg_pp->input_cfg == from)
388 tplg_pp->define_cfg_merged = conf_defines;
389
390 if (tplg_pp->define_cfg_merged == NULL) {
391 create:
392 ret = snd_config_make_compound(&tplg_pp->define_cfg_merged, "Define", 0);
393 if (ret < 0)
394 return ret;
395 ret = snd_config_add(tplg_pp->input_cfg, tplg_pp->define_cfg_merged);
396 if (ret < 0)
397 return ret;
398 }
399
400 if (tplg_pp->define_cfg_merged != conf_defines) {
401 /*
402 * merge back to the main configuration tree (Define subtree)
403 */
404 ret = snd_config_merge(tplg_pp->define_cfg_merged, conf_defines, true);
405 if (ret < 0) {
406 fprintf(stderr, "Failed to override main variable definitions\n");
407 return ret;
408 }
409 }
410
411 /*
412 * merge the command line defines with the variables in the conf file to override
413 * default values; use a copy (merge deletes the source tree)
414 */
415 if (tplg_pp->define_cfg) {
416 ret = snd_config_copy(&conf_tmp, tplg_pp->define_cfg);
417 if (ret < 0) {
418 fprintf(stderr, "Failed to copy variable definitions\n");
419 return ret;
420 }
421 ret = snd_config_merge(tplg_pp->define_cfg_merged, conf_tmp, true);
422 if (ret < 0) {
423 fprintf(stderr, "Failed to override variable definitions\n");
424 snd_config_delete(conf_tmp);
425 return ret;
426 }
427 }
428
429 return 0;
430 }
431
432 static int pre_process_includes(struct tplg_pre_processor *tplg_pp, snd_config_t *top);
433
pre_process_include_conf(struct tplg_pre_processor *tplg_pp, snd_config_t *config, snd_config_t **new, snd_config_t *variable)434 static int pre_process_include_conf(struct tplg_pre_processor *tplg_pp, snd_config_t *config,
435 snd_config_t **new, snd_config_t *variable)
436 {
437 snd_config_iterator_t i, next;
438 const char *variable_name;
439 char *value;
440 int ret;
441
442 if (snd_config_get_id(variable, &variable_name) < 0)
443 return 0;
444
445 switch(snd_config_get_type(variable)) {
446 case SND_CONFIG_TYPE_STRING:
447 {
448 const char *s;
449
450 if (snd_config_get_string(variable, &s) < 0) {
451 SNDERR("Invalid value for variable %s\n", variable_name);
452 return -EINVAL;
453 }
454 value = strdup(s);
455 if (!value)
456 return -ENOMEM;
457 break;
458 }
459 case SND_CONFIG_TYPE_INTEGER:
460 {
461 long v;
462
463 ret = snd_config_get_integer(variable, &v);
464 if (ret < 0) {
465 SNDERR("Invalid value for variable %s\n", variable_name);
466 return ret;
467 }
468
469 value = tplg_snprintf("%ld", v);
470 if (!value)
471 return -ENOMEM;
472 break;
473 }
474 default:
475 SNDERR("Invalid type for variable definition %s\n", variable_name);
476 return -EINVAL;
477 }
478
479 /* create top-level config node */
480 ret = snd_config_top(new);
481 if (ret < 0) {
482 SNDERR("failed to create top-level node for include conf %s\n", variable_name);
483 goto err;
484 }
485
486 snd_config_for_each(i, next, config) {
487 snd_input_t *in;
488 snd_config_t *n;
489 regex_t regex;
490 const char *filename;
491 const char *id;
492 char *full_path;
493
494 n = snd_config_iterator_entry(i);
495 if (snd_config_get_id(n, &id) < 0)
496 continue;
497
498 ret = regcomp(®ex, id, REG_EXTENDED | REG_ICASE);
499 if (ret) {
500 fprintf(stderr, "Could not compile regex\n");
501 goto err;
502 }
503
504 /* Execute regular expression */
505 ret = regexec(®ex, value, 0, NULL, 0);
506 if (ret)
507 continue;
508
509 /* regex matched. now include or use the configuration */
510 if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND) {
511 /* configuration block */
512 ret = snd_config_merge(*new, n, 0);
513 if (ret < 0) {
514 fprintf(stderr, "Unable to merge key '%s'\n", value);
515 goto err;
516 }
517 } else {
518 ret = snd_config_get_string(n, &filename);
519 if (ret < 0)
520 goto err;
521
522 if (filename && filename[0] != '/')
523 full_path = tplg_snprintf("%s/%s", tplg_pp->inc_path, filename);
524 else
525 full_path = tplg_snprintf("%s", filename);
526
527 ret = snd_input_stdio_open(&in, full_path, "r");
528 if (ret < 0) {
529 fprintf(stderr, "Unable to open included conf file %s\n", full_path);
530 free(full_path);
531 goto err;
532 }
533 free(full_path);
534
535 /* load config */
536 ret = snd_config_load(*new, in);
537 snd_input_close(in);
538 if (ret < 0) {
539 fprintf(stderr, "Unable to load included configuration\n");
540 goto err;
541 }
542 }
543
544 /* forcefully overwrite with defines from the command line */
545 ret = pre_process_add_defines(tplg_pp, *new);
546 if (ret < 0 && ret != -ENOENT) {
547 fprintf(stderr, "Failed to parse arguments in input config\n");
548 goto err;
549 }
550
551 /* recursively process any nested includes */
552 ret = pre_process_includes(tplg_pp, *new);
553 if (ret < 0)
554 goto err;
555 }
556
557 err:
558 free(value);
559 return ret;
560 }
561
pre_process_includes(struct tplg_pre_processor *tplg_pp, snd_config_t *top)562 static int pre_process_includes(struct tplg_pre_processor *tplg_pp, snd_config_t *top)
563 {
564 snd_config_iterator_t i, next;
565 snd_config_t *includes;
566 const char *top_id;
567 int ret;
568
569 if (tplg_pp->define_cfg_merged == NULL)
570 return 0;
571
572 ret = snd_config_search(top, "IncludeByKey", &includes);
573 if (ret < 0)
574 return 0;
575
576 snd_config_get_id(top, &top_id);
577
578 snd_config_for_each(i, next, includes) {
579 snd_config_t *n, *new, *define;
580 const char *id;
581
582 n = snd_config_iterator_entry(i);
583 if (snd_config_get_id(n, &id) < 0)
584 continue;
585
586 /* find id from variable definitions */
587 ret = snd_config_search(tplg_pp->define_cfg_merged, id, &define);
588 if (ret < 0) {
589 fprintf(stderr, "No variable defined for %s\n", id);
590 return ret;
591 }
592
593 /* create conf node from included file */
594 ret = pre_process_include_conf(tplg_pp, n, &new, define);
595 if (ret < 0) {
596 fprintf(stderr, "Unable to process include file \n");
597 return ret;
598 }
599
600 /* merge the included conf file with the top-level conf */
601 ret = snd_config_merge(top, new, 0);
602 if (ret < 0) {
603 fprintf(stderr, "Failed to add included conf\n");
604 return ret;
605 }
606 }
607
608 /* delete all includes from current top */
609 snd_config_delete(includes);
610
611 return 0;
612 }
613
pre_process_includes_all(struct tplg_pre_processor *tplg_pp, snd_config_t *top)614 static int pre_process_includes_all(struct tplg_pre_processor *tplg_pp, snd_config_t *top)
615 {
616 snd_config_iterator_t i, next;
617 int ret;
618
619 if (snd_config_get_type(top) != SND_CONFIG_TYPE_COMPOUND)
620 return 0;
621
622 /* process includes at this node */
623 ret = pre_process_includes(tplg_pp, top);
624 if (ret < 0) {
625 fprintf(stderr, "Failed to process includes\n");
626 return ret;
627 }
628
629 /* process includes at all child nodes */
630 snd_config_for_each(i, next, top) {
631 snd_config_t *n;
632
633 n = snd_config_iterator_entry(i);
634
635 ret = pre_process_includes_all(tplg_pp, n);
636 if (ret < 0)
637 return ret;
638 }
639
640 return 0;
641 }
642
643 /* duplicate the existing objects in src into dest and update with new attribute */
pre_process_add_objects(struct tplg_pre_processor *tplg_pp ATTRIBUTE_UNUSED, int *object_count, snd_config_t *src, snd_config_t *dest, snd_config_t *attr_cfg)644 static int pre_process_add_objects(struct tplg_pre_processor *tplg_pp ATTRIBUTE_UNUSED,
645 int *object_count, snd_config_t *src,
646 snd_config_t *dest, snd_config_t *attr_cfg)
647 {
648 snd_config_iterator_t i, next;
649 int ret;
650
651 snd_config_for_each(i, next, src) {
652 snd_config_t *n, *new, *new_attr;
653 char* new_id = tplg_snprintf("%d", (*object_count)++);
654
655 n = snd_config_iterator_entry(i);
656
657 /* duplicate the existing object */
658 ret = snd_config_copy(&new, n);
659 if (ret < 0) {
660 free(new_id);
661 return ret;
662 }
663
664 ret = snd_config_set_id(new, new_id);
665 free(new_id);
666 if (ret < 0) {
667 snd_config_delete(new);
668 return ret;
669 }
670
671 ret = snd_config_add(dest, new);
672 if (ret < 0) {
673 snd_config_delete(new);
674 return ret;
675 }
676
677 /* and update the new attribute */
678 ret = snd_config_copy(&new_attr, attr_cfg);
679 if (ret < 0)
680 return ret;
681
682 ret = snd_config_add(new, new_attr);
683 if (ret < 0) {
684 snd_config_delete(new_attr);
685 return ret;
686 }
687 }
688
689 return 0;
690 }
691
692 /* Create config based on the number of items in the array */
pre_process_create_items(struct tplg_pre_processor *tplg_pp, snd_config_t *cfg, snd_config_t *top, int *object_count_offset)693 static int pre_process_create_items(struct tplg_pre_processor *tplg_pp,
694 snd_config_t *cfg, snd_config_t *top,
695 int *object_count_offset)
696 {
697 snd_config_iterator_t i, next;
698 snd_config_type_t type;
699 const char *class_id;
700 char *class_id_local;
701 int attr_count = 0;
702 int object_count = *object_count_offset;
703 int ret;
704
705 snd_config_get_id(top, &class_id);
706 class_id_local = strdup(class_id);
707
708 snd_config_for_each(i, next, cfg) {
709 snd_config_iterator_t i2, next2;
710 snd_config_t *n, *local_top;
711 const char *attribute;
712 int attr_val_count = 0;
713 object_count = *object_count_offset;
714
715 n = snd_config_iterator_entry(i);
716 type = snd_config_get_type(n);
717 if (type != SND_CONFIG_TYPE_COMPOUND)
718 continue;
719 if (snd_config_get_id(n, &attribute) < 0)
720 continue;
721
722 ret = snd_config_make(&local_top, class_id_local, SND_CONFIG_TYPE_COMPOUND);
723
724 snd_config_for_each(i2, next2, n) {
725 snd_config_t *n2, *new, *new_obj;
726 snd_config_type_t attr_type;
727 char *new_id;
728
729 n2 = snd_config_iterator_entry(i2);
730
731 attr_type = snd_config_get_type(n2);
732
733 /* create new config based on type */
734 if (attr_type == SND_CONFIG_TYPE_INTEGER) {
735 long val;
736
737 ret = snd_config_get_integer(n2, &val);
738 if (ret < 0)
739 return ret;
740
741 ret = snd_config_make(&new, attribute, SND_CONFIG_TYPE_INTEGER);
742 if (ret < 0)
743 return ret;
744
745 ret = snd_config_set_integer(new, val);
746 } else {
747 const char *s;
748
749 ret = snd_config_get_string(n2, &s);
750 if (ret < 0)
751 return ret;
752 ret = snd_config_make(&new, attribute, SND_CONFIG_TYPE_STRING);
753 if (ret < 0)
754 return ret;
755
756 ret = snd_config_set_string(new, s);
757 }
758
759 if (ret < 0)
760 goto err;
761
762 /* for the first array simply create new conf nodes */
763 if (!attr_count) {
764 new_id = tplg_snprintf("%d", object_count++);
765 ret = snd_config_make(&new_obj, new_id, SND_CONFIG_TYPE_COMPOUND);
766 free(new_id);
767
768 ret = snd_config_add(new_obj, new);
769 if (ret < 0) {
770 snd_config_delete(new_obj);
771 goto err;
772 }
773
774 ret = snd_config_add(local_top, new_obj);
775 if (ret < 0) {
776 snd_config_delete(new_obj);
777 goto err;
778 }
779
780 continue;
781 }
782
783 /*
784 * for the subsequent arrays, duplicate the existing objects
785 * and update them with the new ones
786 */
787 ret = pre_process_add_objects(tplg_pp, &object_count, top,
788 local_top, new);
789 if (ret < 0) {
790 SNDERR("failed to add objects of type %s\n", class_id_local);
791 goto err;
792 }
793
794 attr_val_count++;
795 err:
796 snd_config_delete(new);
797 if (ret < 0) {
798 snd_config_delete(local_top);
799 return ret;
800 }
801 }
802
803 /* substitute current list of configs with the updated list */
804 ret = snd_config_substitute(top, local_top);
805 if (ret < 0) {
806 snd_config_delete(local_top);
807 return ret;
808 }
809 attr_count++;
810 }
811
812 *object_count_offset = object_count;
813 return 0;
814 }
815
pre_process_array_item(struct tplg_pre_processor *tplg_pp, snd_config_t *top, snd_config_t *array)816 static int pre_process_array_item(struct tplg_pre_processor *tplg_pp, snd_config_t *top,
817 snd_config_t *array)
818 {
819 snd_config_iterator_t i, next;
820 int object_count;
821 int ret;
822
823 snd_config_for_each(i, next, array) {
824 snd_config_iterator_t i3, next3;
825 snd_config_t *n, *new;
826 const char *id;
827
828 n = snd_config_iterator_entry(i);
829 if (snd_config_get_id(n, &id) < 0)
830 continue;
831
832 /* Create a new node if it doesn't exist already */
833 if (snd_config_search(top, id, &new) < 0) {
834 ret = snd_config_make(&new, id, SND_CONFIG_TYPE_COMPOUND);
835 if (ret < 0)
836 return ret;
837
838 /* add the list of objects to the current top */
839 ret = snd_config_add(top, new);
840 if (ret < 0) {
841 snd_config_delete(new);
842 return ret;
843 }
844 }
845
846 /* if the conf node is not an array, move on to parse child nodes */
847 if (snd_config_is_array(n) <= 0)
848 return pre_process_array_item(tplg_pp, new, n);
849
850 object_count = 0;
851 snd_config_for_each(i3, next3, n) {
852 snd_config_t *n3, *local_top;
853
854 n3 = snd_config_iterator_entry(i3);
855
856 ret = snd_config_make(&local_top, id, SND_CONFIG_TYPE_COMPOUND);
857 if (ret < 0)
858 return ret;
859
860 ret = pre_process_create_items(tplg_pp, n3, local_top,
861 &object_count);
862 if (ret < 0) {
863 SNDERR("failed to create objects of type %s\n", id);
864 return ret;
865 }
866
867 ret = snd_config_merge(new, local_top, 0);
868 if (ret < 0) {
869 snd_config_delete(local_top);
870 return ret;
871 }
872 }
873 }
874
875 return 0;
876 }
877
pre_process_array(struct tplg_pre_processor *tplg_pp, snd_config_t *top)878 static int pre_process_array(struct tplg_pre_processor *tplg_pp, snd_config_t *top)
879 {
880 snd_config_t *arrays;
881 int ret;
882
883 ret = snd_config_search(top, "CombineArrays", &arrays);
884 if (ret < 0)
885 return 0;
886
887 ret = pre_process_array_item(tplg_pp, top, arrays);
888 if (ret < 0)
889 return ret;
890
891 snd_config_delete(arrays);
892 return 0;
893 }
894
pre_process_arrays(struct tplg_pre_processor *tplg_pp, snd_config_t *top)895 static int pre_process_arrays(struct tplg_pre_processor *tplg_pp, snd_config_t *top)
896 {
897 snd_config_iterator_t i, next;
898 int ret;
899
900 if (snd_config_get_type(top) != SND_CONFIG_TYPE_COMPOUND)
901 return 0;
902
903 /* process object arrays at this node */
904 ret = pre_process_array(tplg_pp, top);
905 if (ret < 0) {
906 fprintf(stderr, "Failed to process object arrays\n");
907 return ret;
908 }
909
910 /* process object arrays at all child nodes */
911 snd_config_for_each(i, next, top) {
912 snd_config_t *n;
913
914 n = snd_config_iterator_entry(i);
915
916 ret = pre_process_arrays(tplg_pp, n);
917 if (ret < 0)
918 return ret;
919 }
920
921 return 0;
922 }
923
924 #endif /* version < 1.2.6 */
925
pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size, const char *pre_processor_defs, const char *inc_path)926 int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size,
927 const char *pre_processor_defs, const char *inc_path)
928 {
929 snd_input_t *in;
930 snd_config_t *top;
931 int err;
932
933 /* create input buffer */
934 err = snd_input_buffer_open(&in, config, config_size);
935 if (err < 0) {
936 fprintf(stderr, "Unable to open input buffer\n");
937 return err;
938 }
939
940 /* create top-level config node */
941 err = snd_config_top(&top);
942 if (err < 0)
943 goto input_close;
944
945 /* load config */
946 err = snd_config_load(top, in);
947 if (err < 0) {
948 fprintf(stderr, "Unable not load configuration\n");
949 goto err;
950 }
951
952 tplg_pp->input_cfg = top;
953 tplg_pp->inc_path = inc_path ? strdup(inc_path) : NULL;
954
955 #if SND_LIB_VER(1, 2, 5) < SND_LIB_VERSION
956 /* parse command line definitions */
957 err = pre_process_set_defines(tplg_pp, pre_processor_defs);
958 if (err < 0) {
959 fprintf(stderr, "Failed to parse arguments in input config\n");
960 goto err;
961 }
962
963 /* parse command line definitions */
964 err = pre_process_add_defines(tplg_pp, top);
965 if (err < 0) {
966 fprintf(stderr, "Failed to parse arguments in input config\n");
967 goto err;
968 }
969
970 /* include conditional conf files */
971 err = pre_process_includes_all(tplg_pp, tplg_pp->input_cfg);
972 if (err < 0) {
973 fprintf(stderr, "Failed to process conditional includes in input config\n");
974 goto err;
975 }
976
977 /* expand object arrays */
978 err = pre_process_arrays(tplg_pp, tplg_pp->input_cfg);
979 if (err < 0) {
980 fprintf(stderr, "Failed to process object arrays in input config\n");
981 goto err;
982 }
983 #endif
984
985 err = pre_process_config(tplg_pp, tplg_pp->input_cfg);
986 if (err < 0) {
987 fprintf(stderr, "Unable to pre-process configuration\n");
988 goto err;
989 }
990
991 /* process topology plugins */
992 err = pre_process_plugins(tplg_pp);
993 if (err < 0) {
994 fprintf(stderr, "Unable to run pre-process plugins or plugins return error\n");
995 goto err;
996 }
997
998 /* save config to output */
999 err = snd_config_save(tplg_pp->output_cfg, tplg_pp->output);
1000 if (err < 0)
1001 fprintf(stderr, "failed to save pre-processed output file\n");
1002
1003 err:
1004 snd_config_delete(top);
1005 input_close:
1006 snd_input_close(in);
1007
1008 return err;
1009 }
1010