1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2018 Intel Corporation. All rights reserved.
4 //
5 // Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
6 // Liam Girdwood <liam.r.girdwood@linux.intel.com>
7
8 /* Topology loader to set up components and pipeline */
9
10 #include <dlfcn.h>
11 #include <errno.h>
12 #include <math.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <sof/common.h>
16 #include <rtos/string.h>
17 #include <sof/audio/component.h>
18 #include <sof/ipc/driver.h>
19 #include <sof/ipc/topology.h>
20 #include <tplg_parser/topology.h>
21 #include <tplg_parser/tokens.h>
22 #include "testbench/common_test.h"
23 #include "testbench/file.h"
24
25 #define MAX_TPLG_OBJECT_SIZE 4096
26
27 /* load asrc dapm widget */
tb_register_asrc(struct testbench_prm * tp,struct tplg_context * ctx)28 static int tb_register_asrc(struct testbench_prm *tp, struct tplg_context *ctx)
29 {
30 char tplg_object[MAX_TPLG_OBJECT_SIZE] = {0};
31 struct sof_ipc_comp *comp = (struct sof_ipc_comp *)tplg_object;
32 struct sof *sof = ctx->sof;
33 struct sof_ipc_comp_asrc *asrc;
34 int ret = 0;
35
36 ret = tplg_new_asrc(ctx, comp, MAX_TPLG_OBJECT_SIZE, NULL, 0);
37 if (ret < 0)
38 return ret;
39
40 asrc = (struct sof_ipc_comp_asrc *)comp;
41
42 /* set testbench input and output sample rate from topology */
43 if (!tp->fs_out)
44 tp->fs_out = asrc->sink_rate;
45 else
46 asrc->sink_rate = tp->fs_out;
47
48 if (!tp->fs_in)
49 tp->fs_in = asrc->source_rate;
50 else
51 asrc->source_rate = tp->fs_in;
52
53 /* load asrc component */
54 if (ipc_comp_new(sof->ipc, ipc_to_comp_new(&asrc)) < 0) {
55 fprintf(stderr, "error: new asrc comp\n");
56 return -EINVAL;
57 }
58
59 return ret;
60 }
61
62 /* load buffer DAPM widget */
tb_register_buffer(struct testbench_prm * tp,struct tplg_context * ctx)63 static int tb_register_buffer(struct testbench_prm *tp, struct tplg_context *ctx)
64 {
65 struct sof *sof = ctx->sof;
66 struct sof_ipc_buffer buffer = {{{0}}};
67 int ret;
68
69 ret = tplg_new_buffer(ctx, &buffer, sizeof(buffer),
70 NULL, 0);
71 if (ret < 0)
72 return ret;
73
74 /* create buffer component */
75 if (ipc_buffer_new(sof->ipc, &buffer) < 0) {
76 fprintf(stderr, "error: buffer new\n");
77 return -EINVAL;
78 }
79
80 return 0;
81 }
82
83 /* load pipeline graph DAPM widget*/
tb_register_graph(struct tplg_context * ctx,struct tplg_comp_info * temp_comp_list,char * pipeline_string,int num_comps,int num_connections,int pipeline_id)84 static int tb_register_graph(struct tplg_context *ctx, struct tplg_comp_info *temp_comp_list,
85 char *pipeline_string,
86 int num_comps, int num_connections,
87 int pipeline_id)
88 {
89 struct sof_ipc_pipe_comp_connect connection;
90 struct sof *sof = ctx->sof;
91 int ret = 0;
92 int i;
93
94 for (i = 0; i < num_connections; i++) {
95 ret = tplg_create_graph(ctx, num_comps, pipeline_id, temp_comp_list,
96 pipeline_string, &connection, i);
97 if (ret < 0)
98 return ret;
99
100 /* connect source and sink */
101 if (ipc_comp_connect(sof->ipc, ipc_to_pipe_connect(&connection)) < 0) {
102 fprintf(stderr, "error: comp connect\n");
103 return -EINVAL;
104 }
105 }
106
107 /* pipeline complete after pipeline connections are established */
108 for (i = 0; i < num_comps; i++) {
109 if (temp_comp_list[i].pipeline_id == pipeline_id &&
110 temp_comp_list[i].type == SND_SOC_TPLG_DAPM_SCHEDULER)
111 ipc_pipeline_complete(sof->ipc, temp_comp_list[i].id);
112 }
113
114 return ret;
115 }
116
117 /* load mixer dapm widget */
tb_register_mixer(struct testbench_prm * tp,struct tplg_context * ctx)118 static int tb_register_mixer(struct testbench_prm *tp, struct tplg_context *ctx)
119 {
120 char tplg_object[MAX_TPLG_OBJECT_SIZE] = {0};
121 struct sof_ipc_comp *comp = (struct sof_ipc_comp *)tplg_object;
122 struct sof *sof = ctx->sof;
123 int ret = 0;
124
125 ret = tplg_new_mixer(ctx, comp, MAX_TPLG_OBJECT_SIZE, NULL, 0);
126 if (ret < 0)
127 return ret;
128
129 /* load mixer component */
130 if (ipc_comp_new(sof->ipc, ipc_to_comp_new(comp)) < 0) {
131 fprintf(stderr, "error: new mixer comp\n");
132 ret = -EINVAL;
133 }
134
135 return ret;
136 }
137
tb_register_pga(struct testbench_prm * tp,struct tplg_context * ctx)138 static int tb_register_pga(struct testbench_prm *tp, struct tplg_context *ctx)
139 {
140 char tplg_object[MAX_TPLG_OBJECT_SIZE] = {0};
141 struct sof_ipc_comp *comp = (struct sof_ipc_comp *)tplg_object;
142 struct sof *sof = ctx->sof;
143 int ret;
144
145 ret = tplg_new_pga(ctx, comp, MAX_TPLG_OBJECT_SIZE, NULL, 0);
146 if (ret < 0) {
147 fprintf(stderr, "error: failed to create PGA\n");
148 return ret;
149 }
150
151 /* load volume component */
152 if (ipc_comp_new(sof->ipc, ipc_to_comp_new(comp)) < 0) {
153 fprintf(stderr, "error: new pga comp\n");
154 ret = -EINVAL;
155 }
156
157 return ret;
158 }
159
160 /* load scheduler dapm widget */
tb_register_pipeline(struct testbench_prm * tp,struct tplg_context * ctx)161 static int tb_register_pipeline(struct testbench_prm *tp, struct tplg_context *ctx)
162 {
163 struct sof *sof = ctx->sof;
164 struct sof_ipc_pipe_new pipeline = {0};
165 int ret;
166
167 ret = tplg_new_pipeline(ctx, &pipeline, sizeof(pipeline), NULL);
168 if (ret < 0)
169 return ret;
170
171 pipeline.sched_id = ctx->sched_id;
172
173 /* Create pipeline */
174 if (ipc_pipeline_new(sof->ipc, (ipc_pipe_new *)&pipeline) < 0) {
175 fprintf(stderr, "error: pipeline new\n");
176 return -EINVAL;
177 }
178
179 return 0;
180 }
181
182 /* load process dapm widget */
tb_register_process(struct testbench_prm * tp,struct tplg_context * ctx)183 static int tb_register_process(struct testbench_prm *tp, struct tplg_context *ctx)
184 {
185 struct sof *sof = ctx->sof;
186 struct sof_ipc_comp_process *process;
187 int ret = 0;
188
189 process = calloc(1, MAX_TPLG_OBJECT_SIZE);
190 if (!process)
191 return -ENOMEM;
192
193 ret = tplg_new_process(ctx, process, MAX_TPLG_OBJECT_SIZE, NULL, 0);
194 if (ret < 0)
195 goto out;
196
197 /* Instantiate */
198 ret = ipc_comp_new(sof->ipc, ipc_to_comp_new(process));
199
200 out:
201 free(process);
202 if (ret < 0)
203 fprintf(stderr, "error: new process comp\n");
204
205 return ret;
206 }
207
208 /* load src dapm widget */
tb_register_src(struct testbench_prm * tp,struct tplg_context * ctx)209 static int tb_register_src(struct testbench_prm *tp, struct tplg_context *ctx)
210 {
211 struct sof *sof = ctx->sof;
212 char tplg_object[MAX_TPLG_OBJECT_SIZE] = {0};
213 struct sof_ipc_comp *comp = (struct sof_ipc_comp *)tplg_object;
214 struct sof_ipc_comp_src *src;
215 int ret = 0;
216
217 ret = tplg_new_src(ctx, comp, MAX_TPLG_OBJECT_SIZE, NULL, 0);
218 if (ret < 0)
219 return ret;
220
221 src = (struct sof_ipc_comp_src *)comp;
222
223 /* set testbench input and output sample rate from topology */
224 if (!tp->fs_out)
225 tp->fs_out = src->sink_rate;
226 else
227 src->sink_rate = tp->fs_out;
228
229 if (!tp->fs_in)
230 tp->fs_in = src->source_rate;
231 else
232 src->source_rate = tp->fs_in;
233
234 /* load src component */
235 if (ipc_comp_new(sof->ipc, ipc_to_comp_new(comp)) < 0) {
236 fprintf(stderr, "error: new src comp\n");
237 return -EINVAL;
238 }
239
240 return ret;
241 }
242
243 /* load fileread component */
tb_new_fileread(struct tplg_context * ctx,struct sof_ipc_comp_file * fileread)244 static int tb_new_fileread(struct tplg_context *ctx,
245 struct sof_ipc_comp_file *fileread)
246 {
247 struct snd_soc_tplg_vendor_array *array = &ctx->widget->priv.array[0];
248 size_t total_array_size = 0;
249 int size = ctx->widget->priv.size;
250 int comp_id = ctx->comp_id;
251 char uuid[UUID_SIZE];
252 int ret;
253
254 /* read vendor tokens */
255 while (total_array_size < size) {
256 if (!tplg_is_valid_priv_size(total_array_size, size, array)) {
257 fprintf(stderr, "error: filewrite array size mismatch for widget size %d\n",
258 size);
259 return -EINVAL;
260 }
261
262 /* parse comp tokens */
263 ret = sof_parse_tokens(&fileread->config, comp_tokens,
264 ARRAY_SIZE(comp_tokens), array,
265 array->size);
266 if (ret != 0) {
267 fprintf(stderr, "error: parse comp tokens %d\n",
268 size);
269 return -EINVAL;
270 }
271
272 /* parse uuid token */
273 ret = sof_parse_tokens(uuid, comp_ext_tokens,
274 ARRAY_SIZE(comp_ext_tokens), array,
275 array->size);
276 if (ret != 0) {
277 fprintf(stderr, "error: parse mixer uuid token %d\n", size);
278 return -EINVAL;
279 }
280
281 total_array_size += array->size;
282 array = MOVE_POINTER_BY_BYTES(array, array->size);
283 }
284
285 /* configure fileread */
286 fileread->mode = FILE_READ;
287 fileread->comp.id = comp_id;
288
289 /* use fileread comp as scheduling comp */
290 fileread->comp.core = ctx->core_id;
291 fileread->comp.hdr.size = sizeof(struct sof_ipc_comp_file);
292 fileread->comp.type = SOF_COMP_FILEREAD;
293 fileread->comp.pipeline_id = ctx->pipeline_id;
294 fileread->config.hdr.size = sizeof(struct sof_ipc_comp_config);
295 return 0;
296 }
297
298 /* load filewrite component */
tb_new_filewrite(struct tplg_context * ctx,struct sof_ipc_comp_file * filewrite)299 static int tb_new_filewrite(struct tplg_context *ctx,
300 struct sof_ipc_comp_file *filewrite)
301 {
302 struct snd_soc_tplg_vendor_array *array = &ctx->widget->priv.array[0];
303 size_t total_array_size = 0;
304 int size = ctx->widget->priv.size;
305 int comp_id = ctx->comp_id;
306 char uuid[UUID_SIZE];
307 int ret;
308
309 /* read vendor tokens */
310 while (total_array_size < size) {
311 if (!tplg_is_valid_priv_size(total_array_size, size, array)) {
312 fprintf(stderr, "error: filewrite array size mismatch\n");
313 return -EINVAL;
314 }
315
316 ret = sof_parse_tokens(&filewrite->config, comp_tokens,
317 ARRAY_SIZE(comp_tokens), array,
318 array->size);
319 if (ret != 0) {
320 fprintf(stderr, "error: parse filewrite tokens %d\n",
321 size);
322 return -EINVAL;
323 }
324
325 /* parse uuid token */
326 ret = sof_parse_tokens(uuid, comp_ext_tokens,
327 ARRAY_SIZE(comp_ext_tokens), array,
328 array->size);
329 if (ret != 0) {
330 fprintf(stderr, "error: parse mixer uuid token %d\n", size);
331 return -EINVAL;
332 }
333
334 total_array_size += array->size;
335 array = MOVE_POINTER_BY_BYTES(array, array->size);
336 }
337
338 /* configure filewrite */
339 filewrite->comp.core = ctx->core_id;
340 filewrite->comp.id = comp_id;
341 filewrite->mode = FILE_WRITE;
342 filewrite->comp.hdr.size = sizeof(struct sof_ipc_comp_file);
343 filewrite->comp.type = SOF_COMP_FILEWRITE;
344 filewrite->comp.pipeline_id = ctx->pipeline_id;
345 filewrite->config.hdr.size = sizeof(struct sof_ipc_comp_config);
346 return 0;
347 }
348
349 /* load fileread component */
tb_register_fileread(struct testbench_prm * tp,struct tplg_context * ctx,int dir)350 static int tb_register_fileread(struct testbench_prm *tp,
351 struct tplg_context *ctx, int dir)
352 {
353 struct sof *sof = ctx->sof;
354 struct sof_ipc_comp_file fileread = {{{0}}};
355 int ret;
356
357 fileread.config.frame_fmt = tplg_find_format(tp->bits_in);
358
359 ret = tb_new_fileread(ctx, &fileread);
360 if (ret < 0)
361 return ret;
362
363 /* configure fileread */
364 fileread.fn = strdup(tp->input_file[tp->input_file_index]);
365 if (tp->input_file_index == 0)
366 tp->fr_id = ctx->comp_id;
367
368 /* use fileread comp as scheduling comp */
369 ctx->sched_id = ctx->comp_id;
370 tp->input_file_index++;
371
372 /* Set format from testbench command line*/
373 fileread.rate = tp->fs_in;
374 fileread.channels = tp->channels_in;
375 fileread.frame_fmt = tp->frame_fmt;
376 fileread.direction = dir;
377
378 /* Set type depending on direction */
379 fileread.comp.type = (dir == SOF_IPC_STREAM_PLAYBACK) ?
380 SOF_COMP_HOST : SOF_COMP_DAI;
381
382 /* create fileread component */
383 if (ipc_comp_new(sof->ipc, ipc_to_comp_new(&fileread)) < 0) {
384 fprintf(stderr, "error: file read\n");
385 free(fileread.fn);
386 return -EINVAL;
387 }
388
389 free(fileread.fn);
390 return 0;
391 }
392
393 /* load filewrite component */
tb_register_filewrite(struct testbench_prm * tp,struct tplg_context * ctx,int dir)394 static int tb_register_filewrite(struct testbench_prm *tp,
395 struct tplg_context *ctx, int dir)
396 {
397 struct sof *sof = ctx->sof;
398 struct sof_ipc_comp_file filewrite = {{{0}}};
399 int ret;
400
401 ret = tb_new_filewrite(ctx, &filewrite);
402 if (ret < 0)
403 return ret;
404
405 /* configure filewrite (multiple output files are supported.) */
406 if (!tp->output_file[tp->output_file_index]) {
407 fprintf(stderr, "error: output[%d] file name is null\n",
408 tp->output_file_index);
409 return -EINVAL;
410 }
411 filewrite.fn = strdup(tp->output_file[tp->output_file_index]);
412 if (tp->output_file_index == 0)
413 tp->fw_id = ctx->comp_id;
414 tp->output_file_index++;
415
416 /* Set format from testbench command line*/
417 filewrite.rate = tp->fs_out;
418 filewrite.channels = tp->channels_out;
419 filewrite.frame_fmt = tp->frame_fmt;
420 filewrite.direction = dir;
421
422 /* Set type depending on direction */
423 filewrite.comp.type = (dir == SOF_IPC_STREAM_PLAYBACK) ?
424 SOF_COMP_DAI : SOF_COMP_HOST;
425
426 /* create filewrite component */
427 if (ipc_comp_new(sof->ipc, ipc_to_comp_new(&filewrite)) < 0) {
428 fprintf(stderr, "error: new file write\n");
429 free(filewrite.fn);
430 return -EINVAL;
431 }
432
433 free(filewrite.fn);
434 return 0;
435 }
436
tb_register_aif_in_out(struct testbench_prm * tb,struct tplg_context * ctx,int dir)437 static int tb_register_aif_in_out(struct testbench_prm *tb,
438 struct tplg_context *ctx, int dir)
439 {
440 if (dir == SOF_IPC_STREAM_PLAYBACK)
441 return tb_register_fileread(tb, ctx, dir);
442 else
443 return tb_register_filewrite(tb, ctx, dir);
444 }
445
tb_register_dai_in_out(struct testbench_prm * tb,struct tplg_context * ctx,int dir)446 static int tb_register_dai_in_out(struct testbench_prm *tb,
447 struct tplg_context *ctx, int dir)
448 {
449 if (dir == SOF_IPC_STREAM_PLAYBACK)
450 return tb_register_filewrite(tb, ctx, dir);
451 else
452 return tb_register_fileread(tb, ctx, dir);
453 }
454
455 /*
456 * create a list with all widget info
457 * containing mapping between component names and ids
458 * which will be used for setting up component connections
459 */
tb_insert_comp(struct testbench_prm * tb,struct tplg_context * ctx)460 static inline int tb_insert_comp(struct testbench_prm *tb, struct tplg_context *ctx)
461 {
462 struct tplg_comp_info *temp_comp_list = tb->info;
463 int comp_index = tb->info_index;
464 int comp_id = ctx->comp_id;
465
466 /* mapping should be empty */
467 if (temp_comp_list[comp_index].name) {
468 fprintf(stderr, "comp index %d already in use with %d:%s cant insert %d:%s\n",
469 comp_index,
470 temp_comp_list[comp_index].id, temp_comp_list[comp_index].name,
471 ctx->widget->id, ctx->widget->name);
472 return -EINVAL;
473 }
474
475 temp_comp_list[comp_index].id = comp_id;
476 temp_comp_list[comp_index].name = ctx->widget->name;
477 temp_comp_list[comp_index].type = ctx->widget->id;
478 temp_comp_list[comp_index].pipeline_id = ctx->pipeline_id;
479
480 printf("debug: loading idx %d comp_id %d: widget %s type %d size %d at offset %ld\n",
481 comp_index, comp_id, ctx->widget->name, ctx->widget->id, ctx->widget->size,
482 ctx->tplg_offset);
483
484 return 0;
485 }
486
487 /* load dapm widget */
tb_load_widget(struct testbench_prm * tb,struct tplg_context * ctx)488 static int tb_load_widget(struct testbench_prm *tb, struct tplg_context *ctx)
489 {
490 struct tplg_comp_info *temp_comp_list = tb->info;
491 int comp_id = ctx->comp_id;
492 int ret = 0;
493
494 /* get next widget */
495 ctx->widget = tplg_get_widget(ctx);
496 ctx->widget_size = ctx->widget->size;
497
498 if (!temp_comp_list) {
499 fprintf(stderr, "load_widget: temp_comp_list argument NULL\n");
500 return -EINVAL;
501 }
502
503 /* insert widget into mapping */
504 ret = tb_insert_comp(tb, ctx);
505 if (ret < 0) {
506 fprintf(stderr, "plug_load_widget: invalid widget index\n");
507 return ret;
508 }
509
510 printf("debug: loading comp_id %d: widget %s id %d\n",
511 comp_id, ctx->widget->name, ctx->widget->id);
512
513 /* load widget based on type */
514 switch (ctx->widget->id) {
515 /* load pga widget */
516 case SND_SOC_TPLG_DAPM_PGA:
517 if (tb_register_pga(tb, ctx) < 0) {
518 fprintf(stderr, "error: load pga\n");
519 ret = -EINVAL;
520 goto exit;
521 }
522 break;
523 case SND_SOC_TPLG_DAPM_AIF_IN:
524 if (tb_register_aif_in_out(tb, ctx, SOF_IPC_STREAM_PLAYBACK) < 0) {
525 fprintf(stderr, "error: load AIF IN failed\n");
526 ret = -EINVAL;
527 goto exit;
528 }
529 break;
530 case SND_SOC_TPLG_DAPM_AIF_OUT:
531 if (tb_register_aif_in_out(tb, ctx, SOF_IPC_STREAM_CAPTURE) < 0) {
532 fprintf(stderr, "error: load AIF OUT failed\n");
533 ret = -EINVAL;
534 goto exit;
535 }
536 break;
537 case SND_SOC_TPLG_DAPM_DAI_IN:
538 if (tb_register_dai_in_out(tb, ctx, SOF_IPC_STREAM_PLAYBACK) < 0) {
539 fprintf(stderr, "error: load filewrite\n");
540 ret = -EINVAL;
541 goto exit;
542 }
543 break;
544 case SND_SOC_TPLG_DAPM_DAI_OUT:
545 if (tb_register_dai_in_out(tb, ctx, SOF_IPC_STREAM_CAPTURE) < 0) {
546 fprintf(stderr, "error: load filewrite\n");
547 ret = -EINVAL;
548 goto exit;
549 }
550 break;
551 case SND_SOC_TPLG_DAPM_BUFFER:
552 if (tb_register_buffer(tb, ctx) < 0) {
553 fprintf(stderr, "error: load buffer\n");
554 ret = -EINVAL;
555 goto exit;
556 }
557 break;
558
559 case SND_SOC_TPLG_DAPM_SCHEDULER:
560 if (tb_register_pipeline(tb, ctx) < 0) {
561 fprintf(stderr, "error: load pipeline\n");
562 ret = -EINVAL;
563 goto exit;
564 }
565 break;
566
567 case SND_SOC_TPLG_DAPM_SRC:
568 if (tb_register_src(tb, ctx) < 0) {
569 fprintf(stderr, "error: load src\n");
570 ret = -EINVAL;
571 goto exit;
572 }
573 break;
574 case SND_SOC_TPLG_DAPM_ASRC:
575 if (tb_register_asrc(tb, ctx) < 0) {
576 fprintf(stderr, "error: load src\n");
577 ret = -EINVAL;
578 goto exit;
579 }
580 break;
581 case SND_SOC_TPLG_DAPM_MIXER:
582 if (tb_register_mixer(tb, ctx) < 0) {
583 fprintf(stderr, "error: load mixer\n");
584 ret = -EINVAL;
585 goto exit;
586 }
587 break;
588 case SND_SOC_TPLG_DAPM_EFFECT:
589 if (tb_register_process(tb, ctx) < 0) {
590 fprintf(stderr, "error: load effect\n");
591 ret = -EINVAL;
592 goto exit;
593 }
594 break;
595 /* unsupported widgets */
596 default:
597 printf("info: Widget %s id %d unsupported and skipped: size %d priv size %d\n",
598 ctx->widget->name, ctx->widget->id,
599 ctx->widget->size, ctx->widget->priv.size);
600 break;
601 }
602
603 ret = 1;
604
605 exit:
606 return ret;
607 }
608
609 /* parse topology file and set up pipeline */
tb_parse_topology(struct testbench_prm * tb,struct tplg_context * ctx)610 int tb_parse_topology(struct testbench_prm *tb, struct tplg_context *ctx)
611
612 {
613 struct snd_soc_tplg_hdr *hdr;
614 struct tplg_comp_info *comp_list_realloc = NULL;
615 char pipeline_string[256] = {0};
616 int i;
617 int ret = 0;
618 FILE *file;
619 size_t size;
620
621 /* open topology file */
622 file = fopen(ctx->tplg_file, "rb");
623 if (!file) {
624 fprintf(stderr, "error: can't open topology %s : %s\n", ctx->tplg_file,
625 strerror(errno));
626 return -errno;
627 }
628
629 /* file size */
630 if (fseek(file, 0, SEEK_END)) {
631 fprintf(stderr, "error: can't seek to end of topology: %s\n",
632 strerror(errno));
633 fclose(file);
634 return -errno;
635 }
636 ctx->tplg_size = ftell(file);
637 if (fseek(file, 0, SEEK_SET)) {
638 fprintf(stderr, "error: can't seek to beginning of topology: %s\n",
639 strerror(errno));
640 fclose(file);
641 return -errno;
642 }
643
644 /* load whole topology into memory */
645 ctx->tplg_base = calloc(ctx->tplg_size, 1);
646 if (!ctx->tplg_base) {
647 fprintf(stderr, "error: can't alloc buffer for topology %zu bytes\n",
648 ctx->tplg_size);
649 fclose(file);
650 return -ENOMEM;
651 }
652 ret = fread(ctx->tplg_base, ctx->tplg_size, 1, file);
653 if (ret != 1) {
654 fprintf(stderr, "error: can't read topology: %s\n",
655 strerror(errno));
656 free(ctx->tplg_base);
657 fclose(file);
658 return -errno;
659 }
660 fclose(file);
661
662 while (ctx->tplg_offset < ctx->tplg_size) {
663
664 /* read next topology header */
665 hdr = tplg_get_hdr(ctx);
666
667 fprintf(stdout, "type: %x, size: 0x%x count: %d index: %d\n",
668 hdr->type, hdr->payload_size, hdr->count, hdr->index);
669
670 ctx->hdr = hdr;
671
672 /* parse header and load the next block based on type */
673 switch (hdr->type) {
674 /* load dapm widget */
675 case SND_SOC_TPLG_TYPE_DAPM_WIDGET:
676
677 fprintf(stdout, "number of DAPM widgets %d\n",
678 hdr->count);
679
680 /* update max pipeline_id */
681 ctx->pipeline_id = hdr->index;
682
683 tb->info_elems += hdr->count;
684 size = sizeof(struct tplg_comp_info) * tb->info_elems;
685 comp_list_realloc = (struct tplg_comp_info *)
686 realloc(tb->info, size);
687
688 if (!comp_list_realloc && size) {
689 fprintf(stderr, "error: mem realloc\n");
690 ret = -errno;
691 goto out;
692 }
693 tb->info = comp_list_realloc;
694
695 for (i = (tb->info_elems - hdr->count); i < tb->info_elems; i++)
696 tb->info[i].name = NULL;
697
698 for (tb->info_index = (tb->info_elems - hdr->count);
699 tb->info_index < tb->info_elems;
700 tb->info_index++) {
701 ret = tb_load_widget(tb, ctx);
702 if (ret < 0) {
703 printf("error: loading widget\n");
704 goto out;
705 } else if (ret > 0)
706 ctx->comp_id++;
707 }
708 break;
709
710 /* set up component connections from pipeline graph */
711 case SND_SOC_TPLG_TYPE_DAPM_GRAPH:
712 if (tb_register_graph(ctx, tb->info,
713 pipeline_string,
714 tb->info_elems,
715 hdr->count,
716 hdr->index) < 0) {
717 fprintf(stderr, "error: pipeline graph\n");
718 ret = -EINVAL;
719 goto out;
720 }
721 break;
722
723 default:
724 tplg_skip_hdr_payload(ctx);
725 break;
726 }
727 }
728
729 out:
730 /* free all data */
731 free(tb->info);
732 free(ctx->tplg_base);
733 return ret;
734 }
735
736