1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2018 Intel Corporation. All rights reserved.
4 //
5 // Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
6 //         Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
7 
8 #include <sof/ipc/driver.h>
9 #include <sof/ipc/topology.h>
10 #include <platform/lib/ll_schedule.h>
11 #include <sof/list.h>
12 #include <getopt.h>
13 #include "testbench/common_test.h"
14 #include <tplg_parser/topology.h>
15 #include "testbench/trace.h"
16 #include "testbench/file.h"
17 #include <limits.h>
18 #include <stdlib.h>
19 #include <stdbool.h>
20 
21 #define TESTBENCH_NCH	2
22 
23 /*
24  * Parse output filenames from user input
25  * This function takes in the output filenames as an input in the format:
26  * "output_file1,output_file2,..."
27  * The max supported output filename number is 4, min is 1.
28  */
parse_output_files(char * outputs,struct testbench_prm * tp)29 static int parse_output_files(char *outputs, struct testbench_prm *tp)
30 {
31 	char *output_token = NULL;
32 	char *token = strtok_r(outputs, ",", &output_token);
33 	int index;
34 
35 	for (index = 0; index < MAX_OUTPUT_FILE_NUM && token; index++) {
36 		/* get output file name with current index */
37 		tp->output_file[index] = strdup(token);
38 
39 		/* next output */
40 		token = strtok_r(NULL, ",", &output_token);
41 	}
42 
43 	if (index == MAX_OUTPUT_FILE_NUM && token) {
44 		fprintf(stderr, "error: max output file number is %d\n",
45 			MAX_OUTPUT_FILE_NUM);
46 		for (index = 0; index < MAX_OUTPUT_FILE_NUM; index++)
47 			free(tp->output_file[index]);
48 		return -EINVAL;
49 	}
50 
51 	/* set total output file number */
52 	tp->output_file_num = index;
53 	return 0;
54 }
55 
56 /*
57  * Parse inputfilenames from user input
58  */
parse_input_files(char * inputs,struct testbench_prm * tp)59 static int parse_input_files(char *inputs, struct testbench_prm *tp)
60 {
61 	char *input_token = NULL;
62 	char *token = strtok_r(inputs, ",", &input_token);
63 	int index;
64 
65 	for (index = 0; index < MAX_INPUT_FILE_NUM && token; index++) {
66 		/* get input file name with current index */
67 		tp->input_file[index] = strdup(token);
68 
69 		/* next input */
70 		token = strtok_r(NULL, ",", &input_token);
71 	}
72 
73 	if (index == MAX_INPUT_FILE_NUM && token) {
74 		fprintf(stderr, "error: max input file number is %d\n",
75 			MAX_INPUT_FILE_NUM);
76 		for (index = 0; index < MAX_INPUT_FILE_NUM; index++)
77 			free(tp->input_file[index]);
78 		return -EINVAL;
79 	}
80 
81 	/* set total input file number */
82 	tp->input_file_num = index;
83 	return 0;
84 }
85 
parse_pipelines(char * pipelines,struct testbench_prm * tp)86 static int parse_pipelines(char *pipelines, struct testbench_prm *tp)
87 {
88 	char *output_token = NULL;
89 	char *token = strtok_r(pipelines, ",", &output_token);
90 	int index;
91 
92 	for (index = 0; index < MAX_OUTPUT_FILE_NUM && token; index++) {
93 		/* get output file name with current index */
94 		tp->pipelines[index] = atoi(token);
95 
96 		/* next output */
97 		token = strtok_r(NULL, ",", &output_token);
98 	}
99 
100 	if (index == MAX_OUTPUT_FILE_NUM && token) {
101 		fprintf(stderr, "error: max output file number is %d\n",
102 			MAX_OUTPUT_FILE_NUM);
103 		return -EINVAL;
104 	}
105 
106 	/* set total output file number */
107 	tp->pipeline_num = index;
108 	return 0;
109 }
110 
111 /* print usage for testbench */
print_usage(char * executable)112 static void print_usage(char *executable)
113 {
114 	printf("Usage: %s <options> -i <input_file> ", executable);
115 	printf("-o <output_file1,output_file2,...>\n\n");
116 	printf("Options for processing:\n");
117 	printf("  -t <topology file>\n");
118 	printf("  -a <comp1=comp1_library,comp2=comp2_library>, override default library\n\n");
119 	printf("Options to control test:\n");
120 	printf("  -d Run in debug mode\n");
121 	printf("  -q Run in quiet mode, suppress traces output\n");
122 	printf("  -p <pipeline1,pipeline2,...>\n");
123 	printf("  -s Use real time priorities for threads (needs sudo)\n");
124 	printf("  -C <number of copy() iterations>\n");
125 	printf("  -D <pipeline duration in ms>\n");
126 	printf("  -P <number of dynamic pipeline iterations>\n");
127 	printf("  -T <microseconds for tick, 0 for batch mode>\n");
128 	printf("Options for input and output format override:\n");
129 	printf("  -b <input_format>, S16_LE, S24_LE, or S32_LE\n");
130 	printf("  -c <input channels>\n");
131 	printf("  -n <output channels>\n");
132 	printf("  -r <input rate>\n");
133 	printf("  -R <output rate>\n\n");
134 	printf("Environment variables\n");
135 	printf("  SOF_HOST_CORE0=<i> - Map DSP core 0..N to host i..i+N\n");
136 	printf("Help:\n");
137 	printf("  -h\n\n");
138 	printf("Example Usage:\n");
139 	printf("%s -i in.txt -o out.txt -t test.tplg ", executable);
140 	printf("-r 48000 -R 96000 -c 2 ");
141 	printf("-b S16_LE -a volume=libsof_volume.so\n");
142 }
143 
144 /* free components */
test_pipeline_free_comps(int pipeline_id)145 static void test_pipeline_free_comps(int pipeline_id)
146 {
147 	struct list_item *clist;
148 	struct list_item *temp;
149 	struct ipc_comp_dev *icd = NULL;
150 	int err;
151 
152 	/* remove the components for this pipeline */
153 	list_for_item_safe(clist, temp, &sof_get()->ipc->comp_list) {
154 		icd = container_of(clist, struct ipc_comp_dev, list);
155 
156 		switch (icd->type) {
157 		case COMP_TYPE_COMPONENT:
158 			if (icd->cd->pipeline->pipeline_id != pipeline_id)
159 				break;
160 			err = ipc_comp_free(sof_get()->ipc, icd->id);
161 			if (err)
162 				fprintf(stderr, "failed to free comp %d\n",
163 					icd->id);
164 			break;
165 		case COMP_TYPE_BUFFER:
166 			if (icd->cb->pipeline_id != pipeline_id)
167 				break;
168 			err = ipc_buffer_free(sof_get()->ipc, icd->id);
169 			if (err)
170 				fprintf(stderr, "failed to free buffer %d\n",
171 					icd->id);
172 			break;
173 		default:
174 			if (icd->pipeline->pipeline_id != pipeline_id)
175 				break;
176 			err = ipc_pipeline_free(sof_get()->ipc, icd->id);
177 			if (err)
178 				fprintf(stderr, "failed to free pipeline %d\n",
179 					icd->id);
180 			break;
181 		}
182 	}
183 }
184 
test_pipeline_set_test_limits(int pipeline_id,int max_copies,int max_samples)185 static void test_pipeline_set_test_limits(int pipeline_id, int max_copies,
186 					  int max_samples)
187 {
188 	struct list_item *clist;
189 	struct list_item *temp;
190 	struct ipc_comp_dev *icd = NULL;
191 	struct comp_dev *cd;
192 	struct dai_data *dd;
193 	struct file_comp_data *fcd;
194 
195 	/* set the test limits for this pipeline */
196 	list_for_item_safe(clist, temp, &sof_get()->ipc->comp_list) {
197 		icd = container_of(clist, struct ipc_comp_dev, list);
198 
199 		switch (icd->type) {
200 		case COMP_TYPE_COMPONENT:
201 			cd = icd->cd;
202 			if (cd->pipeline->pipeline_id != pipeline_id)
203 				break;
204 
205 			switch (cd->drv->type) {
206 			case SOF_COMP_HOST:
207 			case SOF_COMP_DAI:
208 			case SOF_COMP_FILEREAD:
209 			case SOF_COMP_FILEWRITE:
210 				/* only file limits supported today. TODO: add others */
211 				dd = comp_get_drvdata(cd);
212 				fcd = comp_get_drvdata(dd->dai);
213 				fcd->max_samples = max_samples;
214 				fcd->max_copies = max_copies;
215 				break;
216 			default:
217 				break;
218 			}
219 			break;
220 		case COMP_TYPE_BUFFER:
221 		default:
222 			break;
223 		}
224 	}
225 }
226 
test_pipeline_get_file_stats(int pipeline_id)227 static void test_pipeline_get_file_stats(int pipeline_id)
228 {
229 	struct list_item *clist;
230 	struct list_item *temp;
231 	struct ipc_comp_dev *icd;
232 	struct comp_dev *cd;
233 	struct dai_data *dd;
234 	struct file_comp_data *fcd;
235 	unsigned long time;
236 
237 	/* get the file IO status for each file in pipeline */
238 	list_for_item_safe(clist, temp, &sof_get()->ipc->comp_list) {
239 		icd = container_of(clist, struct ipc_comp_dev, list);
240 
241 		switch (icd->type) {
242 		case COMP_TYPE_COMPONENT:
243 			cd = icd->cd;
244 			if (cd->pipeline->pipeline_id != pipeline_id)
245 				break;
246 			switch (cd->drv->type) {
247 			case SOF_COMP_HOST:
248 			case SOF_COMP_DAI:
249 			case SOF_COMP_FILEREAD:
250 			case SOF_COMP_FILEWRITE:
251 				dd = comp_get_drvdata(cd);
252 				fcd = comp_get_drvdata(dd->dai);
253 
254 				time = cd->pipeline->pipe_task->start;
255 				if (fcd->fs.copy_count == 0)
256 					fcd->fs.copy_count = 1;
257 				printf("file %s: id %d: type %d: samples %d copies %d total time %zu uS avg time %zu uS\n",
258 				       fcd->fs.fn, cd->ipc_config.id, cd->drv->type, fcd->fs.n,
259 				       fcd->fs.copy_count, time, time / fcd->fs.copy_count);
260 				break;
261 			default:
262 				break;
263 			}
264 			break;
265 		case COMP_TYPE_BUFFER:
266 		default:
267 			break;
268 		}
269 	}
270 }
271 
parse_input_args(int argc,char ** argv,struct testbench_prm * tp)272 static int parse_input_args(int argc, char **argv, struct testbench_prm *tp)
273 {
274 	int option = 0;
275 	int ret = 0;
276 
277 	while ((option = getopt(argc, argv, "hdqi:o:t:b:a:r:R:c:n:C:P:Vp:T:D:")) != -1) {
278 		switch (option) {
279 		/* input sample file */
280 		case 'i':
281 			ret = parse_input_files(optarg, tp);
282 			break;
283 
284 		/* output sample files */
285 		case 'o':
286 			ret = parse_output_files(optarg, tp);
287 			break;
288 
289 		/* topology file */
290 		case 't':
291 			tp->tplg_file = strdup(optarg);
292 			break;
293 
294 		/* input samples bit format */
295 		case 'b':
296 			tp->bits_in = strdup(optarg);
297 			tp->frame_fmt = tplg_find_format(tp->bits_in);
298 			break;
299 
300 		/* input sample rate */
301 		case 'r':
302 			tp->fs_in = atoi(optarg);
303 			break;
304 
305 		/* output sample rate */
306 		case 'R':
307 			tp->fs_out = atoi(optarg);
308 			break;
309 
310 		/* input/output channels */
311 		case 'c':
312 			tp->channels_in = atoi(optarg);
313 			break;
314 
315 		/* output channels */
316 		case 'n':
317 			tp->channels_out = atoi(optarg);
318 			break;
319 
320 			/* enable debug prints */
321 		case 'd':
322 			debug = 1;
323 			break;
324 
325 		/* number of pipeline copy() iterations */
326 		case 'C':
327 			tp->copy_iterations = atoi(optarg);
328 			tp->copy_check = true;
329 			break;
330 
331 		case 'q':
332 			tp->quiet = true;
333 			break;
334 
335 		/* number of dynamic pipeline iterations */
336 		case 'P':
337 			tp->dynamic_pipeline_iterations = atoi(optarg);
338 			break;
339 
340 		/* output sample files */
341 		case 'p':
342 			ret = parse_pipelines(optarg, tp);
343 			break;
344 
345 		/* Microseconds for tick, 0 = batch (tickless) */
346 		case 'T':
347 			tp->tick_period_us = atoi(optarg);
348 			break;
349 
350 		/* pipeline duration in millisec, 0 = realtime (tickless) */
351 		case 'D':
352 			tp->pipeline_duration_ms = atoi(optarg);
353 			break;
354 
355 		/* print usage */
356 		default:
357 			fprintf(stderr, "unknown option %c\n", option);
358 			ret = -EINVAL;
359 			__attribute__ ((fallthrough));
360 		case 'h':
361 			print_usage(argv[0]);
362 			exit(EXIT_SUCCESS);
363 		}
364 
365 		if (ret < 0)
366 			return ret;
367 	}
368 
369 	return ret;
370 }
371 
get_pipeline_by_id(int id)372 static struct pipeline *get_pipeline_by_id(int id)
373 {
374 	struct ipc_comp_dev *pcm_dev;
375 	struct ipc *ipc = sof_get()->ipc;
376 
377 	pcm_dev = ipc_get_ppl_src_comp(ipc, id);
378 	return pcm_dev->cd->pipeline;
379 }
380 
test_pipeline_stop(struct testbench_prm * tp)381 static int test_pipeline_stop(struct testbench_prm *tp)
382 {
383 	struct pipeline *p;
384 	struct ipc *ipc = sof_get()->ipc;
385 	int ret = 0;
386 	int i;
387 
388 	for (i = 0; i < tp->pipeline_num; i++) {
389 		p = get_pipeline_by_id(tp->pipelines[i]);
390 		ret = tb_pipeline_stop(ipc, p);
391 		if (ret < 0)
392 			break;
393 	}
394 
395 	return ret;
396 }
397 
test_pipeline_reset(struct testbench_prm * tp)398 static int test_pipeline_reset(struct testbench_prm *tp)
399 {
400 	struct pipeline *p;
401 	struct ipc *ipc = sof_get()->ipc;
402 	int ret = 0;
403 	int i;
404 
405 	for (i = 0; i < tp->pipeline_num; i++) {
406 		p = get_pipeline_by_id(tp->pipelines[i]);
407 		ret = tb_pipeline_reset(ipc, p);
408 		if (ret < 0)
409 			break;
410 	}
411 
412 	return ret;
413 }
414 
test_pipeline_free(struct testbench_prm * tp)415 static void test_pipeline_free(struct testbench_prm *tp)
416 {
417 	int i;
418 
419 	for (i = 0; i < tp->pipeline_num; i++)
420 		test_pipeline_free_comps(tp->pipelines[i]);
421 }
422 
test_pipeline_params(struct testbench_prm * tp,struct tplg_context * ctx)423 static int test_pipeline_params(struct testbench_prm *tp, struct tplg_context *ctx)
424 {
425 	struct ipc_comp_dev *pcm_dev;
426 	struct pipeline *p;
427 	struct ipc *ipc = sof_get()->ipc;
428 	int ret = 0;
429 	int i;
430 
431 	/* Run pipeline until EOF from fileread */
432 
433 	for (i = 0; i < tp->pipeline_num; i++) {
434 		pcm_dev = ipc_get_ppl_src_comp(ipc, tp->pipelines[i]);
435 		if (!pcm_dev) {
436 			fprintf(stderr, "error: pipeline %d has no source component\n",
437 				tp->pipelines[i]);
438 			return -EINVAL;
439 		}
440 
441 		/* set up pipeline params */
442 		p = pcm_dev->cd->pipeline;
443 
444 		/* input and output sample rate */
445 		if (!tp->fs_in)
446 			tp->fs_in = p->period * p->frames_per_sched;
447 
448 		if (!tp->fs_out)
449 			tp->fs_out = p->period * p->frames_per_sched;
450 
451 		ret = tb_pipeline_params(tp, ipc, p, ctx);
452 		if (ret < 0) {
453 			fprintf(stderr, "error: pipeline params failed: %s\n",
454 				strerror(ret));
455 			return ret;
456 		}
457 	}
458 
459 
460 	return 0;
461 }
462 
test_pipeline_start(struct testbench_prm * tp)463 static int test_pipeline_start(struct testbench_prm *tp)
464 {
465 	struct pipeline *p;
466 	struct ipc *ipc = sof_get()->ipc;
467 	int i;
468 
469 	/* Run pipeline until EOF from fileread */
470 	for (i = 0; i < tp->pipeline_num; i++) {
471 		p = get_pipeline_by_id(tp->pipelines[i]);
472 
473 		/* do we need to apply copy count limit ? */
474 		if (tp->copy_check)
475 			test_pipeline_set_test_limits(tp->pipelines[i], tp->copy_iterations, 0);
476 
477 		/* set pipeline params and trigger start */
478 		if (tb_pipeline_start(ipc, p) < 0) {
479 			fprintf(stderr, "error: pipeline params\n");
480 			return -EINVAL;
481 		}
482 	}
483 
484 	return 0;
485 }
486 
test_pipeline_check_state(struct testbench_prm * tp,int state)487 static bool test_pipeline_check_state(struct testbench_prm *tp, int state)
488 {
489 	struct pipeline *p;
490 	int i;
491 
492 	schedule_ll_run_tasks();
493 
494 	/* Run pipeline until EOF from fileread */
495 	for (i = 0; i < tp->pipeline_num; i++) {
496 		p = get_pipeline_by_id(tp->pipelines[i]);
497 		if (p->pipe_task->state	== state)
498 			return true;
499 	}
500 
501 	return false;
502 }
503 
test_pipeline_load(struct testbench_prm * tp,struct tplg_context * ctx)504 static int test_pipeline_load(struct testbench_prm *tp, struct tplg_context *ctx)
505 {
506 	int ret;
507 
508 	/* setup the thread virtual core config */
509 	memset(ctx, 0, sizeof(*ctx));
510 	ctx->comp_id = 1;
511 	ctx->core_id = 0;
512 	ctx->sof = sof_get();
513 	ctx->tplg_file = tp->tplg_file;
514 	ctx->ipc_major = 3;
515 
516 	/* parse topology file and create pipeline */
517 	ret = tb_parse_topology(tp, ctx);
518 	if (ret < 0)
519 		fprintf(stderr, "error: parsing topology\n");
520 
521 	return ret;
522 }
523 
test_pipeline_stats(struct testbench_prm * tp,struct tplg_context * ctx,uint64_t delta)524 static void test_pipeline_stats(struct testbench_prm *tp,
525 				struct tplg_context *ctx, uint64_t delta)
526 {
527 	int count = 1;
528 	struct ipc_comp_dev *icd;
529 	struct comp_dev *cd;
530 	struct dai_data *dd;
531 	struct pipeline *p;
532 	struct file_comp_data *frcd, *fwcd;
533 	int n_in, n_out;
534 	int i;
535 
536 	/* Get pointer to filewrite */
537 	icd = ipc_get_comp_by_id(sof_get()->ipc, tp->fw_id);
538 	if (!icd) {
539 		fprintf(stderr, "error: failed to get pointers to filewrite\n");
540 		exit(EXIT_FAILURE);
541 	}
542 	cd = icd->cd;
543 	dd = comp_get_drvdata(cd);
544 	fwcd = comp_get_drvdata(dd->dai);
545 
546 	/* Get pointer to fileread */
547 	icd = ipc_get_comp_by_id(sof_get()->ipc, tp->fr_id);
548 	if (!icd) {
549 		fprintf(stderr, "error: failed to get pointers to fileread\n");
550 		exit(EXIT_FAILURE);
551 	}
552 	cd = icd->cd;
553 	dd = comp_get_drvdata(cd);
554 	frcd = comp_get_drvdata(dd->dai);
555 
556 	/* Run pipeline until EOF from fileread */
557 	icd = ipc_get_comp_by_id(sof_get()->ipc, ctx->sched_id);
558 	p = icd->cd->pipeline;
559 
560 	/* input and output sample rate */
561 	if (!tp->fs_in)
562 		tp->fs_in = p->period * p->frames_per_sched;
563 
564 	if (!tp->fs_out)
565 		tp->fs_out = p->period * p->frames_per_sched;
566 
567 	n_in = frcd->fs.n;
568 	n_out = fwcd->fs.n;
569 
570 	/* print test summary */
571 	printf("==========================================================\n");
572 	printf("		           Test Summary %d\n", count);
573 	printf("==========================================================\n");
574 	printf("Test Pipeline:\n");
575 	printf("%s\n", tp->pipeline_string);
576 	test_pipeline_get_file_stats(ctx->pipeline_id);
577 
578 	printf("Input bit format: %s\n", tp->bits_in);
579 	printf("Input sample rate: %d\n", tp->fs_in);
580 	printf("Output sample rate: %d\n", tp->fs_out);
581 	for (i = 0; i < tp->input_file_num; i++) {
582 		printf("Input[%d] read from file: \"%s\"\n",
583 		       i, tp->input_file[i]);
584 	}
585 	for (i = 0; i < tp->output_file_num; i++) {
586 		printf("Output[%d] written to file: \"%s\"\n",
587 		       i, tp->output_file[i]);
588 	}
589 	printf("Input sample (frame) count: %d (%d)\n", n_in, n_in / tp->channels_in);
590 	printf("Output sample (frame) count: %d (%d)\n", n_out, n_out / tp->channels_out);
591 	printf("Total execution time: %zu us, %.2f x realtime\n\n",
592 	       delta, (double)((double)n_out / tp->channels_out / tp->fs_out) * 1000000 / delta);
593 }
594 
595 /*
596  * Tester thread, one for each virtual core. This is NOT the thread that will
597  * execute the virtual core.
598  */
pipline_test(struct testbench_prm * tp)599 static int pipline_test(struct testbench_prm *tp)
600 {
601 	int dp_count = 0;
602 	struct tplg_context ctx;
603 	struct timespec ts;
604 	struct timespec td0, td1;
605 	int err;
606 	int nsleep_time;
607 	int nsleep_limit;
608 	uint64_t delta;
609 
610 	/* build, run and teardown pipelines */
611 	while (dp_count < tp->dynamic_pipeline_iterations) {
612 		fprintf(stdout, "pipeline run %d/%d\n", dp_count,
613 			tp->dynamic_pipeline_iterations);
614 
615 		/* print test summary */
616 		printf("==========================================================\n");
617 		printf("		           Test Start %d\n", dp_count);
618 		printf("==========================================================\n");
619 
620 		err = test_pipeline_load(tp, &ctx);
621 		if (err < 0) {
622 			fprintf(stderr, "error: pipeline load %d failed %d\n",
623 				dp_count, err);
624 			break;
625 		}
626 
627 		err = test_pipeline_params(tp, &ctx);
628 		if (err < 0) {
629 			fprintf(stderr, "error: pipeline params %d failed %d\n",
630 				dp_count, err);
631 			break;
632 		}
633 
634 		err = test_pipeline_start(tp);
635 		if (err < 0) {
636 			fprintf(stderr, "error: pipeline run %d failed %d\n",
637 				dp_count, err);
638 			break;
639 		}
640 		clock_gettime(CLOCK_MONOTONIC, &td0);
641 
642 		/* sleep to let the pipeline work - we exit at timeout OR
643 		 * if copy iterations OR max_samples is reached (whatever first)
644 		 */
645 		nsleep_time = 0;
646 		ts.tv_sec = tp->tick_period_us / 1000000;
647 		ts.tv_nsec = (tp->tick_period_us % 1000000) * 1000;
648 		if (!tp->copy_check)
649 			nsleep_limit = INT_MAX;
650 		else
651 			nsleep_limit = tp->copy_iterations *
652 				       tp->pipeline_duration_ms;
653 
654 		while (nsleep_time < nsleep_limit) {
655 			/* wait for next tick */
656 			err = nanosleep(&ts, &ts);
657 			if (err == 0) {
658 				nsleep_time += tp->tick_period_us; /* sleep fully completed */
659 				if (test_pipeline_check_state(tp, SOF_TASK_STATE_CANCEL)) {
660 					fprintf(stdout, "pipeline cancelled !\n");
661 					break;
662 				}
663 			} else {
664 				if (err == EINTR) {
665 					continue; /* interrupted - keep going */
666 				} else {
667 					printf("error: sleep failed: %s\n", strerror(err));
668 					break;
669 				}
670 			}
671 		}
672 
673 		clock_gettime(CLOCK_MONOTONIC, &td1);
674 		err = test_pipeline_stop(tp);
675 		if (err < 0) {
676 			fprintf(stderr, "error: pipeline stop %d failed %d\n",
677 				dp_count, err);
678 			break;
679 		}
680 
681 		delta = (td1.tv_sec - td0.tv_sec) * 1000000;
682 		delta += (td1.tv_nsec - td0.tv_nsec) / 1000;
683 		test_pipeline_stats(tp, &ctx, delta);
684 
685 		err = test_pipeline_reset(tp);
686 		if (err < 0) {
687 			fprintf(stderr, "error: pipeline stop %d failed %d\n",
688 				dp_count, err);
689 			break;
690 		}
691 
692 		test_pipeline_free(tp);
693 
694 		dp_count++;
695 	}
696 
697 	return 0;
698 }
699 
700 static struct testbench_prm tp;
701 
main(int argc,char ** argv)702 int main(int argc, char **argv)
703 {
704 	int i, err;
705 
706 	/* initialize input and output sample rates, files, etc. */
707 	debug = 0;
708 	tp.fs_in = 0;
709 	tp.fs_out = 0;
710 	tp.bits_in = 0;
711 	tp.tplg_file = NULL;
712 	tp.input_file_num = 0;
713 	tp.output_file_num = 0;
714 	for (i = 0; i < MAX_OUTPUT_FILE_NUM; i++)
715 		tp.output_file[i] = NULL;
716 
717 	for (i = 0; i < MAX_INPUT_FILE_NUM; i++)
718 		tp.input_file[i] = NULL;
719 
720 	tp.channels_in = TESTBENCH_NCH;
721 	tp.channels_out = 0;
722 	tp.max_pipeline_id = 0;
723 	tp.copy_check = false;
724 	tp.quiet = 0;
725 	tp.dynamic_pipeline_iterations = 1;
726 	tp.pipeline_string = calloc(1, DEBUG_MSG_LEN);
727 	tp.pipelines[0] = 1;
728 	tp.pipeline_num = 1;
729 	tp.tick_period_us = 0; /* Execute fast non-real time, for 1 ms tick use -T 1000 */
730 	tp.pipeline_duration_ms = 5000;
731 	tp.copy_iterations = 1;
732 
733 	/* command line arguments*/
734 	err = parse_input_args(argc, argv, &tp);
735 	if (err < 0)
736 		goto out;
737 
738 	if (!tp.channels_out)
739 		tp.channels_out = tp.channels_in;
740 
741 	/* check mandatory args */
742 	if (!tp.tplg_file) {
743 		fprintf(stderr, "topology file not specified, use -t file.tplg\n");
744 		print_usage(argv[0]);
745 		exit(EXIT_FAILURE);
746 	}
747 
748 	if (!tp.input_file_num) {
749 		fprintf(stderr, "input files not specified, use -i file1,file2\n");
750 		print_usage(argv[0]);
751 		exit(EXIT_FAILURE);
752 	}
753 
754 	if (!tp.output_file_num) {
755 		fprintf(stderr, "output files not specified, use -o file1,file2\n");
756 		print_usage(argv[0]);
757 		exit(EXIT_FAILURE);
758 	}
759 
760 	if (!tp.bits_in) {
761 		fprintf(stderr, "input format not specified, use -b format\n");
762 		print_usage(argv[0]);
763 		exit(EXIT_FAILURE);
764 	}
765 
766 	if (tp.quiet)
767 		tb_enable_trace(false); /* reduce trace output */
768 	else
769 		tb_enable_trace(true);
770 
771 	/* initialize ipc and scheduler */
772 	if (tb_setup(sof_get(), &tp) < 0) {
773 		fprintf(stderr, "error: pipeline init\n");
774 		exit(EXIT_FAILURE);
775 	}
776 
777 	/* build, run and teardown pipelines */
778 	pipline_test(&tp);
779 
780 	/* free other core FW services */
781 	tb_free(sof_get());
782 
783 out:
784 	/* free all other data */
785 	free(tp.bits_in);
786 	free(tp.tplg_file);
787 	for (i = 0; i < tp.output_file_num; i++)
788 		free(tp.output_file[i]);
789 
790 	for (i = 0; i < tp.input_file_num; i++)
791 		free(tp.input_file[i]);
792 
793 	free(tp.pipeline_string);
794 
795 	return EXIT_SUCCESS;
796 }
797