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