[Sound-open-firmware] [PATCH 1/3] [RFC]tune: add common testbench common headers and source files

Liam Girdwood liam.r.girdwood at linux.intel.com
Mon May 7 16:00:16 CEST 2018


On Wed, 2018-05-02 at 21:31 -0700, Ranjani Sridharan wrote:
> This patch adds the common header and source files required for all
> component testbench. These files include routines for parsing
> topology files, initializing sof ipc, scheduler and pipeline structures.
> They also provide simpler implementations for some of the SOF features
> such as tracing.
> 

Just minor comments below, could you also add in some comments too around
functions/code blocks that are non trivial.

> Signed-off-by: Ranjani Sridharan <ranjani.sridharan at linux.intel.com>
> ---
>  Makefile.am               |   2 +-
>  configure.ac              |   4 +-
>  tune/Makefile.am          |   1 +
>  tune/common/Makefile.am   |  11 +
>  tune/common/common_test.c | 461 ++++++++++++++++++++++++
>  tune/common/common_test.h | 125 +++++++
>  tune/common/common_tplg.c | 873
> ++++++++++++++++++++++++++++++++++++++++++++++
>  tune/common/common_tplg.h | 195 +++++++++++
>  8 files changed, 1670 insertions(+), 2 deletions(-)
>  create mode 100644 tune/Makefile.am
>  create mode 100644 tune/common/Makefile.am
>  create mode 100644 tune/common/common_test.c
>  create mode 100644 tune/common/common_test.h
>  create mode 100644 tune/common/common_tplg.c
>  create mode 100644 tune/common/common_tplg.h
> 
> diff --git a/Makefile.am b/Makefile.am
> index ea5d746..2f494d9 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -1 +1 @@
> -SUBDIRS = rmbox topology
> +SUBDIRS = rmbox topology tune
> diff --git a/configure.ac b/configure.ac
> index b95c44c..d1c288d 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -12,7 +12,7 @@ dnl Initialize maintainer mode
>  AM_MAINTAINER_MODE([enable])
>  
>  AC_PROG_CC
> -
> +LT_INIT
>  AC_OUTPUT([
>  	Makefile
>  	rmbox/Makefile
> @@ -22,6 +22,8 @@ AC_OUTPUT([
>  	topology/m4/Makefile
>  	topology/sof/Makefile
>  	topology/test/Makefile
> +	tune/Makefile
> +	tune/common/Makefile
>  ])
>  
>  echo "
> diff --git a/tune/Makefile.am b/tune/Makefile.am
> new file mode 100644
> index 0000000..e2e95a6
> --- /dev/null
> +++ b/tune/Makefile.am
> @@ -0,0 +1 @@
> +SUBDIRS = common
> diff --git a/tune/common/Makefile.am b/tune/common/Makefile.am
> new file mode 100644
> index 0000000..e435d07
> --- /dev/null
> +++ b/tune/common/Makefile.am
> @@ -0,0 +1,11 @@
> +noinst_LIBRARIES = libtb_common.a
> +
> +libtb_common_a_SOURCES = \
> +	common_test.c \
> +	common_tplg.c \
> +	fileread.c \
> +	filewrite.c
> +
> +libtb_common_a_CFLAGS = \
> +	-I $(prefix)/include/sof
> +	-g -Wall
> diff --git a/tune/common/common_test.c b/tune/common/common_test.c
> new file mode 100644
> index 0000000..1d4c9e8
> --- /dev/null
> +++ b/tune/common/common_test.c
> @@ -0,0 +1,461 @@
> +/* Simple test bench versions of SOF functions */
> +
> +/*
> + * Copyright (c) 2017, Intel Corporation
> + * All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions are
> met:
> + *   * Redistributions of source code must retain the above copyright
> + *     notice, this list of conditions and the following disclaimer.
> + *   * Redistributions in binary form must reproduce the above copyright
> + *     notice, this list of conditions and the following disclaimer in the
> + *     documentation and/or other materials provided with the distribution.
> + *   * Neither the name of the Intel Corporation nor the
> + *     names of its contributors may be used to endorse or promote products
> + *     derived from this software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
> IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
> + * POSSIBILITY OF SUCH DAMAGE.
> + *
> + * Author: Seppo Ingalsuo <seppo.ingalsuo at linux.intel.com>
> + *         Liam Girdwood <liam.r.girdwood at linux.intel.com>
> + *         Keyon Jie <yang.jie at linux.intel.com>
> + *	   Ranjani Sridharan <ranjani.sridharan at linux.intel.com>
> + */
> +
> +#include <stdint.h>
> +#include <stddef.h>
> +#include <time.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <math.h>
> +#include <sof/task.h>
> +#include <sof/alloc.h>
> +#include <sof/ipc.h>
> +#include <sof/dai.h>
> +#include <sof/dma.h>
> +#include <sof/work.h>
> +#include <sof/wait.h>
> +#include <sof/intel-ipc.h>
> +#include <sof/audio/pipeline.h>
> +#include "common_test.h"
> +#include "common_tplg.h"
> +
> +/* simplified host test bench versions for features */
> +struct dai *dai_get(uint32_t type, uint32_t index)
> +{
> +	return NULL;
> +}
> +
> +struct dma *dma_get(int dmac_id)
> +{
> +	return NULL;
> +}
> +

Is this to satisfy the linker ? if best to comment it (and any others).

> +/* testbench ipc */
> +struct ipc *_ipc;
> +
> +int ipc_stream_send_position(struct comp_dev *cdev,
> +	struct sof_ipc_stream_posn *posn)
> +{
> +	return 0;
> +}
> +
> +int platform_ipc_init(struct ipc *ipc)
> +{
> +	struct intel_ipc_data *iipc;
> +	int i;
> +
> +	_ipc = ipc;
> +
> +	/* init ipc data */
> +	iipc = malloc(sizeof(struct intel_ipc_data));
> +	ipc_set_drvdata(_ipc, iipc);
> +	_ipc->dsp_msg = NULL;
> +	list_init(&ipc->empty_list);
> +	list_init(&ipc->msg_list);
> +	spinlock_init(&ipc->lock);
> +
> +	for (i = 0; i < MSG_QUEUE_SIZE; i++)
> +		list_item_prepend(&ipc->message[i].list, &ipc->empty_list);
> +
> +	/* allocate page table buffer */
> +	iipc->page_table = malloc(HOST_PAGE_SIZE);
> +	if (iipc->page_table)
> +		bzero(iipc->page_table, HOST_PAGE_SIZE);
> +
> +	/* PM */
> +	iipc->pm_prepare_D3 = 0;
> +
> +	return 0;
> +}
> +
> +int ipc_stream_send_xrun(struct comp_dev *cdev,
> +	struct sof_ipc_stream_posn *posn)
> +{
> +	return 0;
> +}
> +
> +/* testbench trace definition */
> +
> +void _trace_error_atomic(uint32_t event)
> +{
> +}
> +
> +void _trace_event_mbox_atomic(uint32_t event)
> +{
> +}
> +
> +static int test_bench_trace = 1;
> +
> +static char *get_trace_class(uint32_t trace_class)
> +{
> +	switch (trace_class) {
> +	case 1:
> +		return "irq";
> +	case 2:
> +		return "ipc";
> +	case 3:
> +		return "pipe";
> +	case 4:
> +		return "host";
> +	case 5:
> +		return "dai";
> +	case 6:
> +		return "dma";
> +	case 7:
> +		return "ssp";
> +	case 8:
> +		return "comp";
> +	case 9:
> +		return "wait";
> +	case 10:
> +		return "lock";
> +	case 11:
> +		return "mem";
> +	case 12:
> +		return "mixer";
> +	case 13:
> +		return "buffer";
> +	case 14:
> +		return "volume";
> +	case 15:
> +		return "switch";
> +	case 16:
> +		return "mux";
> +	case 17:
> +		return "src";
> +	case 18:
> +		return "tone";
> +	case 19:
> +		return "eq_fir";
> +	case 20:
> +		return "eq_iir";
> +	case 21:
> +		return "sa";
> +	default:
> +		return "value";
> +	}
> +}

How will we keep this in sync with trace (for new types) ?

> +
> +static char replace_blank(char x)

Can you comment what this does (and any others).

> +{
> +	char y = x;
> +
> +	if ((y < '!') || (y > 'z'))
> +		y = ' ';
> +
> +	return y;
> +}
> +
> +void _trace_event(uint32_t event)
> +{
> +	char a, b, c;
> +	char trace_class[10];
> +
> +	if (test_bench_trace > 0) {
> +		a = replace_blank((char) (event & 0xff));
> +		b = replace_blank((char) ((event >> 8) & 0xff));
> +		c = replace_blank((char) ((event >> 16) & 0xff));
> +		strcpy(trace_class, get_trace_class(event >> 24));
> +		if (strcmp(trace_class, "value") == 0)
> +			printf("Trace value %d\n", event);
> +		else
> +			printf("Trace %s %c%c%c\n", trace_class, c, b, a);
> +	}
> +}
> +
> +void _trace_error(uint32_t event)
> +{
> +	char a, b, c;
> +
> +	if (test_bench_trace > 0) {
> +		a = replace_blank((char) (event & 0xff));
> +		b = replace_blank((char) ((event >> 8) & 0xff));
> +		c = replace_blank((char) ((event >> 16) & 0xff));
> +		printf("trace error: %s %c%c%c\n", get_trace_class(event >>
> 24),
> +			c, b, a);
> +	}
> +}
> +
> +void tb_enable_trace(void)
> +{
> +	test_bench_trace = 1;
> +	printf("trace print enabled\n");
> +}
> +
> +void tb_disable_trace(void)
> +{
> +	test_bench_trace = 0;
> +	printf("trace print disabled\n");
> +}
> +
> +/* testbench mem alloc definition */
> +
> +void *rmalloc(int zone, uint32_t caps, size_t bytes)
> +{
> +	return malloc(bytes);
> +}
> +
> +void *rzalloc(int zone, uint32_t caps, size_t bytes)
> +{
> +	void *x;
> +
> +	x = malloc(bytes);
> +	bzero(x, bytes);
> +	return x;
> +}
> +
> +void rfree(void *ptr)
> +{
> +	free(ptr);
> +}
> +
> +void *rballoc(int zone, uint32_t caps, size_t bytes)
> +{
> +	return malloc(bytes);
> +}
> +
> +void rbfree(void *ptr)
> +{
> +	free(ptr);
> +}
> +
> +void *xthal_memcpy(void *dest, const void *src, size_t size)
> +{
> +	return memcpy(dest, src, size);
> +}

These can all be static inline wrappers for posix versions.

> +
> +/* scheduler testbench definition */
> +
> +struct schedule_data {
> +	spinlock_t lock;
> +	struct list_item list; /* list of tasks in priority queue */
> +	uint32_t clock;
> +};
> +static struct schedule_data *sch;
> +
> +void schedule_task_complete(struct task *task)
> +{
> +
> +	list_item_del(&task->list);
> +	task->state = TASK_STATE_COMPLETED;
> +}
> +
> +void schedule_task(struct task *task, uint64_t start, uint64_t deadline)
> +{
> +
> +	task->deadline = deadline;
> +	list_item_prepend(&task->list, &sch->list);
> +	task->state = TASK_STATE_QUEUED;
> +
> +	if (task->func)
> +		task->func(task->data);
> +
> +	schedule_task_complete(task);
> +}
> +
> +void schedule(void)
> +{
> +}
> +
> +int scheduler_init(struct sof *sof)
> +{
> +	trace_pipe("ScI");
> +
> +	sch = malloc(sizeof(*sch));
> +	list_init(&sch->list);
> +	spinlock_init(&sch->lock);
> +
> +	return 0;
> +}
> +
> +void schedule_task_idle(struct task *task, uint64_t deadline)
> +{
> +}
> +
> +/* testbench work definition */
> +
> +void work_schedule_default(struct work *w, uint64_t timeout)
> +{
> +}
> +
> +void work_cancel_default(struct work *work)
> +{
> +}
> +
> +/* testbench helper functions for pipeline setup and trigger */
> +
> +int tb_pipeline_setup(struct sof *sof)
> +{
> +
> +	/* init components */
> +	sys_comp_init();
> +
> +	/* init IPC */
> +	if (ipc_init(sof) < 0) {
> +		fprintf(stderr, "error: IPC init\n");
> +		return -EINVAL;
> +	}
> +
> +	/* init scheduler */
> +	if (scheduler_init(sof) < 0) {
> +		fprintf(stderr, "error: scheduler init\n");
> +		return -EINVAL;
> +	}
> +
> +	/* init pipeline system */
> +	if (pipeline_init() < 0) {
> +		fprintf(stderr, "error: pipeline init\n");
> +		return -EINVAL;
> +	}
> +
> +	debug_print("ipc, scheduler and pipeline initialized\n");
> +
> +	return 0;
> +}
> +
> +/* set up pcm params, prepare and trigger pipeline */
> +int tb_pipeline_start(struct ipc *ipc, int nch, char *bits_in,
> +		      struct sof_ipc_pipe_new *ipc_pipe)
> +{
> +	struct ipc_comp_dev *pcm_dev;
> +	struct pipeline *p;
> +	struct comp_dev *cd;
> +	int ret;
> +
> +	ret = tb_pipeline_params(ipc, nch, bits_in, ipc_pipe);
> +	if (ret < 0) {
> +		printf("error: pipeline params\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Get IPC component device for pipeline */
> +	pcm_dev = ipc_get_comp(ipc, ipc_pipe->sched_id);
> +	if (pcm_dev == NULL) {
> +		printf("error: ipc get comp\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Point to pipeline */
> +	cd = pcm_dev->cd;
> +	p = pcm_dev->cd->pipeline;
> +
> +	/* Component prepare */
> +	ret = pipeline_prepare(p, cd);
> +
> +	/* Start the pipeline */
> +	ret = pipeline_trigger(p, cd, COMP_TRIGGER_START);
> +	if (ret < 0)
> +		printf("Warning: Failed start pipeline command.\n");
> +
> +	return ret;
> +}
> +
> +/* pipeline pcm params */
> +int tb_pipeline_params(struct ipc *ipc, int nch, char *bits_in,
> +		       struct sof_ipc_pipe_new *ipc_pipe)
> +{
> +	int fs_period, ret = 0;
> +	struct ipc_comp_dev *pcm_dev;
> +	struct pipeline *p;
> +	struct comp_dev *cd;
> +	struct sof_ipc_pcm_params params;
> +	int fs, deadline;
> +
> +	deadline = ipc_pipe->deadline;
> +	fs = deadline * ipc_pipe->frames_per_sched;
> +
> +	/* Compute period from sample rates */
> +	fs_period = (int) (0.9999 + fs * deadline / 1e6);
> +
> +	params.comp_id = ipc_pipe->comp_id;
> +	params.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED;
> +	params.params.frame_fmt = find_format(bits_in);
> +	params.params.direction = SOF_IPC_STREAM_PLAYBACK;
> +	params.params.rate = fs;
> +	params.params.channels = nch;
> +	switch (params.params.frame_fmt) {
> +	case(SOF_IPC_FRAME_S16_LE):
> +		params.params.sample_container_bytes = 2;
> +		params.params.sample_valid_bytes = 2;
> +		params.params.host_period_bytes = fs_period * nch *
> +			params.params.sample_container_bytes;
> +		break;
> +	case(SOF_IPC_FRAME_S24_4LE):
> +		params.params.sample_container_bytes = 4;
> +		params.params.sample_valid_bytes = 3;
> +		params.params.host_period_bytes = fs_period * nch *
> +			params.params.sample_container_bytes;
> +		break;
> +	case(SOF_IPC_FRAME_S32_LE):
> +		params.params.sample_container_bytes = 4;
> +		params.params.sample_valid_bytes = 4;
> +		params.params.host_period_bytes = fs_period * nch *
> +			params.params.sample_container_bytes;
> +		break;
> +	default:
> +		printf("error: invalid frame format\n");
> +		return -EINVAL;
> +	}
> +
> +	/* get scheduling component device for pipeline*/
> +	pcm_dev = ipc_get_comp(ipc, ipc_pipe->sched_id);
> +	if (pcm_dev == NULL) {
> +		printf("error: ipc get comp\n");
> +		return -EINVAL;
> +	}
> +
> +	/* point to pipeline */
> +	cd = pcm_dev->cd;
> +	p = pcm_dev->cd->pipeline;
> +	if (p == NULL) {
> +		printf("error: pipeline NULL\n");
> +		return -EINVAL;
> +	}
> +
> +	/* pipeline params */
> +	ret = pipeline_params(p, cd, &params);
> +	if (ret < 0)
> +		printf("error: pipeline_params\n");
> +
> +	return ret;
> +}
> +
> +void debug_print(char *message)
> +{
> +#ifdef DEBUG_PRINT
> +		printf("debug: %s", message);
> +#endif
> +}


I think it's probably worth splitting this file up by subsystem (as currently
split by file in the FW).

> diff --git a/tune/common/common_test.h b/tune/common/common_test.h
> new file mode 100644
> index 0000000..03968cd
> --- /dev/null
> +++ b/tune/common/common_test.h
> @@ -0,0 +1,125 @@
> +/*
> + * Copyright (c) 2017, Intel Corporation
> + * All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions are
> met:
> + *   * Redistributions of source code must retain the above copyright
> + *     notice, this list of conditions and the following disclaimer.
> + *   * Redistributions in binary form must reproduce the above copyright
> + *     notice, this list of conditions and the following disclaimer in the
> + *     documentation and/or other materials provided with the distribution.
> + *   * Neither the name of the Intel Corporation nor the
> + *     names of its contributors may be used to endorse or promote products
> + *     derived from this software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
> IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
> + * POSSIBILITY OF SUCH DAMAGE.
> + *
> + * Author: Seppo Ingalsuo <seppo.ingalsuo at linux.intel.com>
> + *         Liam Girdwood <liam.r.girdwood at linux.intel.com>
> + *         Keyon Jie <yang.jie at linux.intel.com>
> + *	   Ranjani Sridharan <ranjani.sridharan at linux.intel.com>
> + */
> +#ifndef _COMMON_TEST_H
> +#define _COMMON_TEST_H
> +
> +#include <stdint.h>
> +#include <stddef.h>
> +#include <time.h>
> +#include <stdio.h>
> +#include <uapi/ipc.h>
> +#include <sof/sof.h>
> +#include <sof/audio/component.h>
> +#include <sof/audio/format.h>
> +
> +#define FILEREAD_FN_MAXLENGTH	256
> +#define FILEWRITE_FN_MAXLENGTH	256
> +#define DEBUG_MSG_LEN		256
> +
> +/* fileread component state */
> +struct fileread_state {
> +	char fn[FILEREAD_FN_MAXLENGTH];
> +	FILE *fh;
> +	int reached_eof;
> +	int n;
> +};
> +
> +/* fileread comp data */
> +struct fileread_comp_data {
> +	uint32_t period_bytes;
> +	uint32_t channels;
> +	uint32_t frame_bytes;
> +	uint32_t rate;
> +	struct fileread_state frs;
> +	int (*fileread_func)(struct comp_dev *dev, struct comp_buffer *sink,
> +		struct comp_buffer *source, uint32_t frames);
> +
> +};
> +
> +/* filewrite component state */
> +struct filewrite_state {
> +	char fn[FILEWRITE_FN_MAXLENGTH];
> +	FILE *fh;
> +	int write_fail;
> +	int n;
> +	int (*filewrite_func)(struct comp_dev *dev, struct comp_buffer *sink,
> +		struct comp_buffer *source, uint32_t frames);
> +
> +};
> +
> +/* fileread comp data */
> +struct filewrite_comp_data {
> +	uint32_t period_bytes;
> +	uint32_t channels;
> +	uint32_t frame_bytes;
> +	uint32_t rate;
> +	struct filewrite_state fws;
> +	int (*filewrite_func)(struct comp_dev *dev, struct comp_buffer *sink,
> +		struct comp_buffer *source, uint32_t frames);
> +
> +};
> +
> +/* fileread/filewrite ipc comp */
> +struct sof_ipc_comp_fileread {
> +	struct sof_ipc_comp comp;
> +	struct sof_ipc_comp_config config;
> +	char *fn;
> +};
> +
> +struct sof_ipc_comp_filewrite {
> +	struct sof_ipc_comp comp;
> +	struct sof_ipc_comp_config config;
> +	char *fn;
> +};
> +
> +int scheduler_init(struct sof *sof);
> +
> +void sys_comp_fileread_init(void);
> +
> +void sys_comp_filewrite_init(void);
> +
> +int tb_pipeline_setup(struct sof *sof);
> +
> +int tb_pipeline_start(struct ipc *ipc, int nch, char *bits_in,
> +			     struct sof_ipc_pipe_new *ipc_pipe);
> +
> +int tb_pipeline_params(struct ipc *ipc, int nch, char *bits_in,
> +		       struct sof_ipc_pipe_new *ipc_pipe);
> +
> +void tb_enable_trace(void);
> +
> +void tb_disable_trace(void);
> +
> +void debug_print(char *message);
> +
> +#endif
> diff --git a/tune/common/common_tplg.c b/tune/common/common_tplg.c
> new file mode 100644
> index 0000000..f59a386
> --- /dev/null
> +++ b/tune/common/common_tplg.c
> @@ -0,0 +1,873 @@
> +/*
> + * Copyright (c) 2017, Intel Corporation
> + * All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions are
> met:
> + *   * Redistributions of source code must retain the above copyright
> + *     notice, this list of conditions and the following disclaimer.
> + *   * Redistributions in binary form must reproduce the above copyright
> + *     notice, this list of conditions and the following disclaimer in the
> + *     documentation and/or other materials provided with the distribution.
> + *   * Neither the name of the Intel Corporation nor the
> + *     names of its contributors may be used to endorse or promote products
> + *     derived from this software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
> IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
> + * POSSIBILITY OF SUCH DAMAGE.
> + *
> + * Author: Ranjani Sridharan <ranjani.sridharan at linux.intel.com>
> + *	   Liam Girdwood <liam.r.girdwood at linux.intel.com>
> + */
> +
> +/*
> + * Topology parser to parse topology bin file
> + * and set up components and pipeline
> + */
> +
> +#include "common_tplg.h"
> +#include <sof/ipc.h>
> +#include <stdio.h>
> +#include <string.h>
> +
> +FILE *file;
> +char *input_file;
> +char *output_file;
> +
> +/* read vendor tuples array from topology */
> +static int read_array(struct snd_soc_tplg_vendor_array *array)
> +{
> +	int j, ret = 0;
> +	size_t size;
> +
> +	switch (array->type) {
> +	case SND_SOC_TPLG_TUPLE_TYPE_UUID:
> +		;

extra ; (and below too)

> +		/* copy uuid elems into array */
> +		struct snd_soc_tplg_vendor_uuid_elem uuid;
> +
> +		for (j = 0; j < array->num_elems; j++) {
> +			size = sizeof(struct snd_soc_tplg_vendor_uuid_elem);
> +			ret = fread(&uuid, size, 1, file);
> +			if (ret != 1)
> +				return -EINVAL;
> +			memcpy(&array->uuid[j], &uuid, size);
> +		}
> +		break;
> +	case SND_SOC_TPLG_TUPLE_TYPE_STRING:
> +		;
> +		/* copy string elems into array */
> +		struct snd_soc_tplg_vendor_string_elem string;
> +
> +		for (j = 0; j < array->num_elems; j++) {
> +			size = sizeof(struct
> snd_soc_tplg_vendor_string_elem);
> +			ret = fread(&string, size, 1, file);
> +			if (ret != 1)
> +				return -EINVAL;
> +			memcpy(&array->string[j], &string, size);
> +		}
> +		break;
> +	case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
> +	case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
> +	case SND_SOC_TPLG_TUPLE_TYPE_WORD:
> +	case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
> +		;
> +		/* copy value elems into array */
> +		struct snd_soc_tplg_vendor_value_elem value;
> +
> +		for (j = 0; j < array->num_elems; j++) {
> +			size = sizeof(struct snd_soc_tplg_vendor_value_elem);
> +			ret = fread(&value, size, 1, file);
> +			if (ret != 1)
> +				return -EINVAL;
> +			memcpy(&array->value[j], &value, size);
> +		}
> +		break;
> +	default:
> +		printf("error: unknown token type %d\n", array->type);
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +/* load pipeline graph DAPM widget*/
> +static int load_graph(struct sof *sof, struct comp_info *temp_comp_list,
> +		      int count, int num_comps, int pipeline_id)
> +{
> +	struct sof_ipc_pipe_comp_connect connection;
> +	struct snd_soc_tplg_dapm_graph_elem *graph_elem;
> +	size_t size;
> +	int i, j, ret = 0;
> +
> +	/* allocate memory for graph elem */
> +	size = sizeof(struct snd_soc_tplg_dapm_graph_elem);
> +	graph_elem = (struct snd_soc_tplg_dapm_graph_elem *)malloc(size);
> +	if (!graph_elem) {
> +		printf("error: mem alloc\n");
> +		return -EINVAL;
> +	}
> +
> +	/* set up component connections */
> +	connection.source_id = connection.sink_id = -1;
> +	for (i = 0; i < count; i++) {
> +		size = sizeof(struct snd_soc_tplg_dapm_graph_elem);
> +		ret = fread(graph_elem, size, 1, file);
> +		if (ret != 1)
> +			return -EINVAL;
> +		for (j = 0; j < num_comps; j++) {
> +			/* look up component id from the component list */
> +			if (strcmp(temp_comp_list[j].name,
> +				   graph_elem->source) == 0)
> +				connection.source_id = temp_comp_list[j].id;
> +			if (strcmp(temp_comp_list[j].name,
> +				   graph_elem->sink) == 0)
> +				connection.sink_id = temp_comp_list[j].id;
> +		}
> +		/* connect source and sink */
> +		if (connection.source_id != -1 && connection.sink_id != -1)
> +			if (ipc_comp_connect(sof->ipc, &connection) < 0) {
> +				fprintf(stderr, "error: comp connect\n");
> +				return -EINVAL;
> +			}
> +	}
> +
> +	/* pipeline complete after pipeline connections are established */
> +	for (i = 0; i < num_comps; i++) {
> +		if (temp_comp_list[i].pipeline_id == pipeline_id &&
> +			temp_comp_list[i].type ==
> SND_SOC_TPLG_DAPM_SCHEDULER)
> +			ipc_pipeline_complete(sof->ipc,
> temp_comp_list[i].id);
> +	}
> +
> +	free(graph_elem);
> +	return 0;
> +}
> +
> +/* load buffer DAPM widget */
> +static int load_buffer(struct sof *sof, int comp_id, int pipeline_id, int
> size)
> +{
> +	struct sof_ipc_buffer buffer;
> +	struct snd_soc_tplg_vendor_array *array = NULL;
> +	int ret = 0;
> +
> +	/* configure buffer */
> +	buffer.comp.id = comp_id;
> +	buffer.comp.pipeline_id = pipeline_id;
> +
> +	/* allocate memory for vendor tuple array */
> +	array = (struct snd_soc_tplg_vendor_array *)malloc(size);
> +	ret = fread(array, sizeof(struct snd_soc_tplg_vendor_array), 1,
> file);
> +	if (ret != 1)
> +		return -EINVAL;
> +
> +	read_array(array);
> +	/* parse buffer tokens */
> +	ret = sof_parse_tokens(&buffer, buffer_tokens,
> +			       ARRAY_SIZE(buffer_tokens), array,
> +			       size);
> +
> +	/* create buffer component */
> +	if (ipc_buffer_new(sof->ipc, &buffer) < 0) {
> +		fprintf(stderr, "error: buffer new\n");
> +		return -EINVAL;
> +	}
> +	free(array);
> +	return 0;
> +}
> +
> +/* load fileread component */
> +static int load_fileread(struct sof *sof, int comp_id, int pipeline_id,
> +			 int size, char *bits_in, int *fr_id, int *sched_id)
> +{
> +	struct sof_ipc_comp_fileread fileread;
> +	struct snd_soc_tplg_vendor_array *array = NULL;
> +	size_t total_array_size = 0, read_size;
> +	int ret = 0;
> +
> +	fileread.config.frame_fmt = find_format(bits_in);
> +
> +	/* allocate memory for vendor tuple array */
> +	array = (struct snd_soc_tplg_vendor_array *)malloc(size);
> +	if (!array) {
> +		printf("error: mem alloc\n");
> +		return -EINVAL;
> +	}
> +
> +	/* read vendor tokens */
> +	while (total_array_size < size) {
> +		read_size = sizeof(struct snd_soc_tplg_vendor_array);
> +		ret = fread(array, read_size, 1, file);
> +		if (ret != 1)
> +			return -EINVAL;
> +		read_array(array);
> +		/* parse comp tokens */
> +		ret = sof_parse_tokens(&fileread.config, comp_tokens,
> +			       ARRAY_SIZE(comp_tokens), array,
> +			       array->size);
> +		if (ret != 0) {
> +			printf("error: parse fileread tokens %d\n", size);
> +			return -EINVAL;
> +		}
> +		total_array_size += array->size;
> +	}
> +
> +	/* configure fileread */
> +	fileread.fn = input_file;
> +	fileread.comp.id = comp_id;
> +	/* use fileread comp as scheduling comp */
> +	*fr_id = *sched_id = comp_id;
> +	fileread.comp.hdr.size = sizeof(struct sof_ipc_comp_fileread);
> +	fileread.comp.type = SOF_COMP_FILEREAD;
> +	fileread.comp.pipeline_id = pipeline_id;
> +
> +	/* create fileread component */
> +	if (ipc_comp_new(sof->ipc, (struct sof_ipc_comp *)&fileread) < 0) {
> +		fprintf(stderr, "error: comp register\n");
> +		return -EINVAL;
> +	}
> +
> +	free(array);
> +	return 0;
> +}
> +
> +/* load filewrite component */
> +static int load_filewrite(struct sof *sof, int comp_id, int pipeline_id,
> +			 int size, int *fw_id)
> +{
> +
> +	struct sof_ipc_comp_filewrite filewrite;
> +	struct snd_soc_tplg_vendor_array *array = NULL;
> +	size_t total_array_size = 0, read_size;
> +	int ret = 0;
> +
> +	/* allocate memory for vendor tuple array */
> +	array = (struct snd_soc_tplg_vendor_array *)malloc(size);
> +	if (!array) {
> +		printf("error: mem alloc\n");
> +		return -EINVAL;
> +	}
> +
> +	/* read vendor tokens */
> +	while (total_array_size < size) {
> +		read_size = sizeof(struct snd_soc_tplg_vendor_array);
> +		ret = fread(array, read_size, 1, file);
> +		if (ret != 1)
> +			return -EINVAL;
> +
> +		read_array(array);
> +		/* parse comp tokens */
> +		ret = sof_parse_tokens(&filewrite.config, comp_tokens,
> +			       ARRAY_SIZE(comp_tokens), array,
> +			       array->size);
> +		if (ret != 0) {
> +			printf("error: parse filewrite tokens %d\n", size);
> +			return -EINVAL;
> +		}
> +		total_array_size += array->size;
> +	}
> +
> +	/* configure filewrite */
> +	filewrite.fn = output_file;
> +	filewrite.comp.id = comp_id;
> +	*fw_id = comp_id;
> +	filewrite.comp.hdr.size = sizeof(struct sof_ipc_comp_filewrite);
> +	filewrite.comp.type = SOF_COMP_FILEWRITE;
> +	filewrite.comp.pipeline_id = pipeline_id;
> +
> +	/* create filewrite component */
> +	if (ipc_comp_new(sof->ipc, (struct sof_ipc_comp *)&filewrite) < 0) {
> +		fprintf(stderr, "error: comp register\n");
> +		return -EINVAL;
> +	}
> +
> +	free(array);
> +	return 0;
> +}
> +
> +/* load pda dapm widget */
> +static int load_pga(struct sof *sof, int comp_id, int pipeline_id,
> +		    int size)
> +{
> +	struct sof_ipc_comp_volume volume;
> +	struct snd_soc_tplg_vendor_array *array = NULL;
> +	size_t total_array_size = 0, read_size;
> +	int ret = 0;
> +
> +	/* allocate memory for vendor tuple array */
> +	array = (struct snd_soc_tplg_vendor_array *)malloc(size);
> +	if (!array) {
> +		printf("error: mem alloc\n");
> +		return -EINVAL;
> +	}
> +
> +	/* read vendor tokens */
> +	while (total_array_size < size) {
> +		read_size = sizeof(struct snd_soc_tplg_vendor_array);
> +		ret = fread(array, read_size, 1, file);
> +		if (ret != 1)
> +			return -EINVAL;
> +		read_array(array);
> +
> +		/* parse volume tokens */
> +		ret = sof_parse_tokens(&volume.config, comp_tokens,
> +			       ARRAY_SIZE(comp_tokens), array,
> +			       array->size);
> +		if (ret != 0) {
> +			printf("error: parse pga tokens %d\n", size);
> +			return -EINVAL;
> +		}
> +		total_array_size += array->size;
> +	}
> +
> +	/* configure volume */
> +	volume.comp.id = comp_id;
> +	volume.comp.hdr.size = sizeof(struct sof_ipc_comp_volume);
> +	volume.comp.type = SOF_COMP_VOLUME;
> +	volume.comp.pipeline_id = pipeline_id;
> +
> +	/* load volume component */
> +	if (ipc_comp_new(sof->ipc, (struct sof_ipc_comp *)&volume) < 0) {
> +		fprintf(stderr, "error: comp register\n");
> +		return -EINVAL;
> +	}
> +
> +	free(array);
> +	return 0;
> +}
> +
> +/* load scheduler dapm widget */
> +static int load_pipeline(struct sof *sof, struct sof_ipc_pipe_new *pipeline,
> +			 int comp_id, int pipeline_id, int size, int
> *sched_id)
> +{
> +	struct snd_soc_tplg_vendor_array *array = NULL;
> +	size_t total_array_size = 0, read_size;
> +	int ret = 0;
> +
> +	/* configure pipeline */
> +	pipeline->sched_id = *sched_id;
> +	pipeline->comp_id = comp_id;
> +	pipeline->pipeline_id = pipeline_id;
> +
> +	/* allocate memory for vendor tuple array */
> +	array = (struct snd_soc_tplg_vendor_array *)malloc(size);
> +	if (!array) {
> +		printf("error: mem alloc\n");
> +		return -EINVAL;
> +	}
> +
> +	/* read vendor array */
> +	while (total_array_size < size) {
> +		read_size = sizeof(struct snd_soc_tplg_vendor_array);
> +		ret = fread(array, read_size, 1, file);
> +		if (ret != 1)
> +			return -EINVAL;
> +		ret = read_array(array);
> +		if (ret < 0)
> +			return -EINVAL;
> +		/* parse scheduler tokens */
> +		ret = sof_parse_tokens(pipeline, sched_tokens,
> +			       ARRAY_SIZE(sched_tokens), array,
> +			       array->size);
> +		if (ret != 0) {
> +			printf("error: parse pipeline tokens %d\n", size);
> +			return -EINVAL;
> +		}
> +		total_array_size += array->size;
> +	}
> +
> +	/* Create pipeline */
> +	if (ipc_pipeline_new(sof->ipc, pipeline) < 0) {
> +		fprintf(stderr, "error: pipeline new\n");
> +		return -EINVAL;
> +	}
> +
> +	free(array);
> +	return 0;
> +}
> +
> +/* load dapm widget kcontrols
> + * we dont use controls in the testbench atm.
> + * so just skip to the next dapm widget
> + */
> +static int load_controls(struct sof *sof, int num_kcontrols)
> +{
> +	struct snd_soc_tplg_ctl_hdr *ctl_hdr;
> +	struct snd_soc_tplg_mixer_control *mixer_ctl;
> +	struct snd_soc_tplg_enum_control *enum_ctl;
> +	struct snd_soc_tplg_bytes_control *bytes_ctl;
> +	size_t read_size, size;
> +	int j, ret = 0;
> +
> +	/* allocate memory */
> +	size = sizeof(struct snd_soc_tplg_ctl_hdr);
> +	ctl_hdr = (struct snd_soc_tplg_ctl_hdr *)malloc(size);
> +	if (!ctl_hdr) {
> +		printf("error: mem alloc\n");
> +		return -EINVAL;
> +	}
> +
> +	size = sizeof(struct snd_soc_tplg_mixer_control);
> +	mixer_ctl = (struct snd_soc_tplg_mixer_control *)malloc(size);
> +	if (!mixer_ctl) {
> +		printf("error: mem alloc\n");
> +		return -EINVAL;
> +	}
> +
> +	size = sizeof(struct snd_soc_tplg_enum_control);
> +	enum_ctl = (struct snd_soc_tplg_enum_control *)malloc(size);
> +	if (!enum_ctl) {
> +		printf("error: mem alloc\n");
> +		return -EINVAL;
> +	}
> +
> +	size = sizeof(struct snd_soc_tplg_bytes_control);
> +	bytes_ctl = (struct snd_soc_tplg_bytes_control *)malloc(size);
> +	if (!bytes_ctl) {
> +		printf("error: mem alloc\n");
> +		return -EINVAL;
> +	}
> +
> +	for (j = 0; j < num_kcontrols; j++) {
> +		/* read control header */
> +		read_size = sizeof(struct snd_soc_tplg_ctl_hdr);
> +		ret = fread(ctl_hdr, read_size, 1, file);
> +		if (ret != 1)
> +			return -EINVAL;
> +		/* load control based on type */
> +		switch (ctl_hdr->ops.info) {
> +		case SND_SOC_TPLG_CTL_VOLSW:
> +		case SND_SOC_TPLG_CTL_STROBE:
> +		case SND_SOC_TPLG_CTL_VOLSW_SX:
> +		case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
> +		case SND_SOC_TPLG_CTL_RANGE:
> +		case SND_SOC_TPLG_DAPM_CTL_VOLSW:
> +			/* load mixer type control */
> +			read_size = sizeof(struct snd_soc_tplg_ctl_hdr);
> +			fseek(file, read_size * -1, SEEK_CUR);
> +			read_size = sizeof(struct
> snd_soc_tplg_mixer_control);
> +			ret = fread(mixer_ctl, read_size, 1, file);
> +			if (ret != 1)
> +				return -EINVAL;
> +			/* skip mixer private data */
> +			fseek(file, mixer_ctl->priv.size, SEEK_CUR);
> +			break;
> +		case SND_SOC_TPLG_CTL_ENUM:
> +		case SND_SOC_TPLG_CTL_ENUM_VALUE:
> +		case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
> +		case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
> +		case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
> +			/* load enum type control */
> +			read_size = sizeof(struct snd_soc_tplg_ctl_hdr);
> +			fseek(file, read_size * -1, SEEK_CUR);
> +			read_size = sizeof(struct snd_soc_tplg_enum_control);
> +			ret = fread(enum_ctl, read_size, 1, file);
> +			if (ret != 1)
> +				return -EINVAL;
> +			/* skip enum private data */
> +			fseek(file, enum_ctl->priv.size, SEEK_CUR);
> +			break;
> +		case SND_SOC_TPLG_CTL_BYTES:
> +			/* load bytes type controls */
> +			read_size = sizeof(struct snd_soc_tplg_ctl_hdr);
> +			fseek(file, read_size * -1, SEEK_CUR);
> +			read_size = sizeof(struct
> snd_soc_tplg_bytes_control);
> +			ret = fread(bytes_ctl, read_size, 1, file);
> +			if (ret != 1)
> +				return -EINVAL;
> +			/* skip bytes private data */
> +			fseek(file, bytes_ctl->priv.size, SEEK_CUR);
> +			break;
> +		default:
> +			printf("control type not supported\n");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/* free all data */
> +	free(mixer_ctl);
> +	free(enum_ctl);
> +	free(bytes_ctl);
> +	free(ctl_hdr);
> +	return 0;
> +}
> +
> +/* load dapm widget */
> +static int load_widget(struct sof *sof, int *fr_id, int *fw_id,
> +		   int *sched_id, char *bits_in,
> +		   struct comp_info *temp_comp_list,
> +		   struct sof_ipc_pipe_new *pipeline, int comp_id,
> +		   int comp_index, int pipeline_id)
> +{
> +	struct snd_soc_tplg_dapm_widget *widget;
> +	char message[DEBUG_MSG_LEN];
> +	size_t read_size, size;
> +	int ret = 0;
> +
> +	/* allocate memory for widget */
> +	size = sizeof(struct snd_soc_tplg_dapm_widget);
> +	widget = (struct snd_soc_tplg_dapm_widget *)malloc(size);
> +	if (!widget) {
> +		printf("error: mem alloc\n");
> +		return -EINVAL;
> +	}
> +
> +	/* read widget data */
> +	read_size = sizeof(struct snd_soc_tplg_dapm_widget);
> +	ret = fread(widget, read_size, 1, file);
> +	if (ret != 1)
> +		return -EINVAL;
> +
> +	/*
> +	 * create a list with all widget info
> +	 * containing mapping between component names and ids
> +	 * which will be used for setting up component connections
> +	 */
> +	temp_comp_list[comp_index].id = comp_id;
> +	strcpy(temp_comp_list[comp_index].name, widget->name);
> +	temp_comp_list[comp_index].type = widget->id;
> +	temp_comp_list[comp_index].pipeline_id = pipeline_id;
> +
> +	sprintf(message, "loading widget %s id %d\n",
> +		temp_comp_list[comp_index].name,
> +		temp_comp_list[comp_index].id);
> +	debug_print(message);
> +
> +	/* load widget based on type */
> +	switch (temp_comp_list[comp_index].type) {
> +	case(SND_SOC_TPLG_DAPM_PGA):
> +		/* load pga widget */
> +		sys_comp_volume_init();
> +		if (load_pga(sof, temp_comp_list[comp_index].id,
> +			     pipeline_id, widget->priv.size) < 0) {
> +			printf("error: load pga\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case(SND_SOC_TPLG_DAPM_AIF_IN):
> +		sys_comp_fileread_init();
> +		/* replace pcm playback component with fileread in testbench
> */
> +		if (load_fileread(sof, temp_comp_list[comp_index].id,
> +				  pipeline_id, widget->priv.size, bits_in,
> +				  fr_id, sched_id) < 0) {
> +			printf("error: load fileread\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case(SND_SOC_TPLG_DAPM_DAI_IN):
> +		sys_comp_filewrite_init();
> +		/* replace dai in component with filewrite in testbench */
> +		if (load_filewrite(sof, temp_comp_list[comp_index].id,
> +				   pipeline_id, widget->priv.size,
> +				   fw_id) < 0) {
> +			printf("error: load filewrite\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case(SND_SOC_TPLG_DAPM_BUFFER):
> +		/* load buffer */
> +		if (load_buffer(sof, temp_comp_list[comp_index].id,
> +				pipeline_id, widget->priv.size) < 0) {
> +			printf("error: load buffer\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case(SND_SOC_TPLG_DAPM_SCHEDULER):
> +		/* load pipeline */
> +		if (load_pipeline(sof, pipeline,
> +				  temp_comp_list[comp_index].id,
> +				  pipeline_id,
> +				  widget->priv.size,
> +				  sched_id) < 0) {
> +			printf("error: load buffer\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		printf("Widget type not supported %d\n",
> +		       widget->id);
> +		break;
> +	}
> +	/* load widget kcontrols */
> +	if (widget->num_kcontrols > 0)
> +		if (load_controls(sof, widget->num_kcontrols) < 0) {
> +			printf("error: load buffer\n");
> +			return -EINVAL;
> +		}
> +	free(widget);
> +	return 0;
> +}
> +
> +/* parse topology file and set up pipeline */
> +int parse_topology(char *filename, struct sof *sof, int *fr_id, int *fw_id,
> +		   int *sched_id, char *bits_in, char *in_file,
> +		   char *out_file)
> +{
> +	struct snd_soc_tplg_hdr *hdr;
> +
> +	struct comp_info *temp_comp_list = NULL;
> +	struct sof_ipc_pipe_new pipeline;
> +	char message[DEBUG_MSG_LEN];
> +	int next_comp_id = 0, num_comps = 0;
> +	int i, ret = 0;
> +	size_t file_size, size;
> +
> +	/* open topology file */
> +	file = fopen(filename, "rb");
> +	if (!file) {
> +		fprintf(stderr, "error: opening file %s", filename);
> +		return -EINVAL;
> +	}
> +
> +	/* set up fileread and  filewrite file names */
> +	input_file = malloc(strlen(in_file));
> +	strcpy(input_file, in_file);
> +	output_file = malloc(strlen(out_file));
> +	strcpy(output_file, out_file);
> +
> +	/* file size */
> +	fseek(file, 0, SEEK_END);
> +	file_size = ftell(file);
> +	fseek(file, 0, SEEK_SET);
> +
> +	/* allocate memory */
> +	size = sizeof(struct snd_soc_tplg_hdr);
> +	hdr = (struct snd_soc_tplg_hdr *)malloc(size);
> +	if (!hdr) {
> +		printf("error: mem alloc\n");
> +		return -EINVAL;
> +	}
> +
> +	debug_print("topology parsing start\n");
> +	while (1) {
> +		/* read topology header */
> +		ret = fread(hdr, sizeof(struct snd_soc_tplg_hdr), 1, file);
> +		if (ret != 1)
> +			return -EINVAL;
> +
> +		sprintf(message, "type: %x, size: 0x%x count: %d index:
> %d\n",
> +			hdr->type, hdr->payload_size, hdr->count, hdr-
> >index);
> +		debug_print(message);
> +
> +		/* parse header and load the next block based on type */
> +		switch (hdr->type) {
> +		case SND_SOC_TPLG_TYPE_DAPM_WIDGET:
> +			/* load dapm widget */
> +			sprintf(message, "number of DAPM widgets %d\n",
> +				hdr->count);
> +			debug_print(message);
> +			size = sizeof(struct comp_info) * hdr->count;
> +			temp_comp_list = (struct comp_info *)malloc(size);
> +			num_comps = hdr->count;
> +
> +			for (i = 0; i < hdr->count; i++)
> +				load_widget(sof, fr_id, fw_id, sched_id,
> +					    bits_in, temp_comp_list,
> +					    &pipeline, next_comp_id++,
> +					    i, hdr->index);
> +			break;
> +		case SND_SOC_TPLG_TYPE_DAPM_GRAPH:
> +			/* set up component connections from pipeline graph
> */
> +			if (load_graph(sof, temp_comp_list, hdr->count,
> +				       num_comps, hdr->index) < 0) {
> +				printf("error: pipeline graph\n");
> +				return -EINVAL;
> +			}
> +
> +			if (ftell(file) == file_size)
> +				goto finish;
> +			break;
> +		default:
> +			fseek(file, hdr->payload_size, SEEK_CUR);
> +			if (ftell(file) == file_size)
> +				goto finish;
> +			break;
> +		}
> +	}
> +finish:
> +	debug_print("topology parsing end\n");
> +	/* free all data */
> +	free(hdr);
> +	free(temp_comp_list);
> +	fclose(file);
> +	return 0;
> +}
> +
> +/* parse vendor tokens in topology */
> +int sof_parse_tokens(void *object, const struct sof_topology_token *tokens,
> +		     int count, struct snd_soc_tplg_vendor_array *array,
> +		     int priv_size)
> +{
> +	int asize;
> +
> +	while (priv_size > 0) {
> +		asize = array->size;
> +
> +		/* validate asize */
> +		if (asize < 0) { /* FIXME: A zero-size array makes no sense
> */
> +			printf("error: invalid array size 0x%x\n", asize);
> +			return -EINVAL;
> +		}
> +
> +		/* make sure there is enough data before parsing */
> +		priv_size -= asize;
> +
> +		if (priv_size < 0) {
> +			printf("error: invalid array size 0x%x\n", asize);
> +			return -EINVAL;
> +		}
> +
> +		/* call correct parser depending on type */
> +		switch (array->type) {
> +		case SND_SOC_TPLG_TUPLE_TYPE_UUID:
> +			sof_parse_uuid_tokens(object, tokens, count,
> +					      array);
> +			break;
> +		case SND_SOC_TPLG_TUPLE_TYPE_STRING:
> +			sof_parse_string_tokens(object, tokens, count,
> +						array);
> +			break;
> +		case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
> +		case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
> +		case SND_SOC_TPLG_TUPLE_TYPE_WORD:
> +		case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
> +			sof_parse_word_tokens(object, tokens, count,
> +					      array);
> +			break;
> +		default:
> +			printf("error: unknown token type %d\n", array-
> >type);
> +			return -EINVAL;
> +		}
> +
> +		/* next array */
> +		array = (void *)array + asize;
> +	}
> +	return 0;
> +}
> +
> +void sof_parse_word_tokens(void *object,
> +			   const struct sof_topology_token *tokens,
> +			   int count,
> +			   struct snd_soc_tplg_vendor_array *array)
> +{
> +	struct snd_soc_tplg_vendor_value_elem *elem;
> +	int i, j;
> +
> +	/* parse element by element */
> +	for (i = 0; i < array->num_elems; i++) {
> +		elem = &array->value[i];
> +
> +		/* search for token */
> +		for (j = 0; j < count; j++) {
> +			/* match token type */
> +			if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_WORD)
> +				continue;
> +
> +			/* match token id */
> +			if (tokens[j].token != elem->token)
> +				continue;
> +
> +			/* matched - now load token */
> +			tokens[j].get_token(elem, object, tokens[j].offset,
> +					    tokens[j].size);
> +		}
> +	}
> +}
> +
> +void sof_parse_uuid_tokens(void *object,
> +			   const struct sof_topology_token *tokens,
> +			   int count,
> +			   struct snd_soc_tplg_vendor_array *array)
> +{
> +	struct snd_soc_tplg_vendor_uuid_elem *elem;
> +	int i, j;
> +
> +	/* parse element by element */
> +	for (i = 0; i < array->num_elems; i++) {
> +		elem = &array->uuid[i];
> +
> +		/* search for token */
> +		for (j = 0; j < count; j++) {
> +			/* match token type */
> +			if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_UUID)
> +				continue;
> +
> +			/* match token id */
> +			if (tokens[j].token != elem->token)
> +				continue;
> +
> +			/* matched - now load token */
> +			tokens[j].get_token(elem, object, tokens[j].offset,
> +					    tokens[j].size);
> +		}
> +	}
> +}
> +
> +void sof_parse_string_tokens(void *object,
> +			     const struct sof_topology_token *tokens,
> +			     int count,
> +			     struct snd_soc_tplg_vendor_array *array)
> +{
> +	struct snd_soc_tplg_vendor_string_elem *elem;
> +	int i, j;
> +
> +	/* parse element by element */
> +	for (i = 0; i < array->num_elems; i++) {
> +		elem = &array->string[i];
> +
> +		/* search for token */
> +		for (j = 0; j < count; j++) {
> +			/* match token type */
> +			if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_STRING)
> +				continue;
> +
> +			/* match token id */
> +			if (tokens[j].token != elem->token)
> +				continue;
> +
> +			/* matched - now load token */
> +			tokens[j].get_token(elem, object, tokens[j].offset,
> +					    tokens[j].size);
> +		}
> +	}
> +}
> +
> +enum sof_ipc_frame find_format(const char *name)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(sof_frames); i++) {
> +		if (strcmp(name, sof_frames[i].name) == 0)
> +			return sof_frames[i].frame;
> +	}
> +
> +	/* use s32le if nothing is specified */
> +	return SOF_IPC_FRAME_S32_LE;
> +}
> +
> +int get_token_uint32_t(void *elem, void *object, uint32_t offset,
> +		       uint32_t size)
> +{
> +	struct snd_soc_tplg_vendor_value_elem *velem = elem;
> +	uint32_t *val = object + offset;
> +
> +	*val = velem->value;
> +	return 0;
> +}
> +
> +int get_token_comp_format(void *elem, void *object, uint32_t offset,
> +			  uint32_t size)
> +{
> +	struct snd_soc_tplg_vendor_string_elem *velem = elem;
> +	uint32_t *val = object + offset;
> +
> +	*val = find_format(velem->string);
> +	return 0;
> +}
> diff --git a/tune/common/common_tplg.h b/tune/common/common_tplg.h
> new file mode 100644
> index 0000000..ea7d12c
> --- /dev/null
> +++ b/tune/common/common_tplg.h
> @@ -0,0 +1,195 @@
> +/*
> + * Copyright (c) 2017, Intel Corporation
> + * All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions are
> met:
> + *   * Redistributions of source code must retain the above copyright
> + *     notice, this list of conditions and the following disclaimer.
> + *   * Redistributions in binary form must reproduce the above copyright
> + *     notice, this list of conditions and the following disclaimer in the
> + *     documentation and/or other materials provided with the distribution.
> + *   * Neither the name of the Intel Corporation nor the
> + *     names of its contributors may be used to endorse or promote products
> + *     derived from this software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
> IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
> + * POSSIBILITY OF SUCH DAMAGE.
> + *
> + * Author: Liam Girdwood <liam.r.girdwood at linux.intel.com>
> + *	   Ranjani Sridharan <ranjani.sridharan at linux.intel.com>
> + */
> +#ifndef _COMMON_TPLG_H
> +#define _COMMON_TPLG_H
> +
> +#include <sound/asoc.h>
> +#include "common_test.h"
> +
> +/*
> + * Tokens - must match values in topology configurations
> + */
> +
> +/* buffers */
> +#define SOF_TKN_BUF_SIZE                        100
> +#define SOF_TKN_BUF_CAPS                        101
> +
> +/* scheduling */
> +#define SOF_TKN_SCHED_DEADLINE                  200
> +#define SOF_TKN_SCHED_PRIORITY                  201
> +#define SOF_TKN_SCHED_MIPS                      202
> +#define SOF_TKN_SCHED_CORE                      203
> +#define SOF_TKN_SCHED_FRAMES                    204
> +#define SOF_TKN_SCHED_TIMER                     205
> +
> +/* volume */
> +#define SOF_TKN_VOLUME_RAMP_STEP_TYPE           250
> +#define SOF_TKN_VOLUME_RAMP_STEP_MS             251
> +
> +/* SRC */
> +#define SOF_TKN_SRC_RATE_IN                     300
> +#define SOF_TKN_SRC_RATE_OUT                    301
> +
> +/* Generic components */
> +#define SOF_TKN_COMP_PERIOD_SINK_COUNT          400
> +#define SOF_TKN_COMP_PERIOD_SOURCE_COUNT        401
> +#define SOF_TKN_COMP_FORMAT                     402
> +#define SOF_TKN_COMP_PRELOAD_COUNT              403
> +
> +struct comp_info {
> +	char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
> +	int id;
> +	int type;
> +	int pipeline_id;
> +};
> +
> +struct frame_types {
> +	char *name;
> +	enum sof_ipc_frame frame;
> +};
> +
> +static const struct frame_types sof_frames[] = {
> +	{"s16le", SOF_IPC_FRAME_S16_LE},
> +	{"s24le", SOF_IPC_FRAME_S24_4LE},
> +	{"s32le", SOF_IPC_FRAME_S32_LE},
> +	{"float", SOF_IPC_FRAME_FLOAT},
> +};
> +
> +struct sof_topology_token {
> +	uint32_t token;
> +	uint32_t type;
> +	int (*get_token)(void *elem, void *object, uint32_t offset,
> +			 uint32_t size);
> +	uint32_t offset;
> +	uint32_t size;
> +};
> +
> +enum sof_ipc_frame find_format(const char *name);
> +
> +int get_token_uint32_t(void *elem, void *object, uint32_t offset,
> +		       uint32_t size);
> +
> +int get_token_comp_format(void *elem, void *object, uint32_t offset,
> +			  uint32_t size);
> +
> +/* Buffers */
> +static const struct sof_topology_token buffer_tokens[] = {
> +	{SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_uint32_t,
> +		offsetof(struct sof_ipc_buffer, size), 0},
> +	{SOF_TKN_BUF_CAPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_uint32_t,
> +		offsetof(struct sof_ipc_buffer, caps), 0},
> +};
> +
> +/* scheduling */
> +static const struct sof_topology_token sched_tokens[] = {
> +	{SOF_TKN_SCHED_DEADLINE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
> +		get_token_uint32_t,
> +		offsetof(struct sof_ipc_pipe_new, deadline), 0},
> +	{SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD,
> +		get_token_uint32_t,
> +		offsetof(struct sof_ipc_pipe_new, priority), 0},
> +	{SOF_TKN_SCHED_MIPS, SND_SOC_TPLG_TUPLE_TYPE_WORD,
> +		get_token_uint32_t,
> +		offsetof(struct sof_ipc_pipe_new, mips), 0},
> +	{SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
> +		get_token_uint32_t,
> +		offsetof(struct sof_ipc_pipe_new, core), 0},
> +	{SOF_TKN_SCHED_FRAMES, SND_SOC_TPLG_TUPLE_TYPE_WORD,
> +		get_token_uint32_t,
> +		offsetof(struct sof_ipc_pipe_new, frames_per_sched), 0},
> +	{SOF_TKN_SCHED_TIMER, SND_SOC_TPLG_TUPLE_TYPE_WORD,
> +		get_token_uint32_t,
> +		offsetof(struct sof_ipc_pipe_new, timer), 0},
> +};
> +
> +/* volume */
> +static const struct sof_topology_token volume_tokens[] = {
> +	{SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
> +		get_token_uint32_t,
> +		offsetof(struct sof_ipc_comp_volume, ramp), 0},
> +	{SOF_TKN_VOLUME_RAMP_STEP_MS,
> +		SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_uint32_t,
> +		offsetof(struct sof_ipc_comp_volume, initial_ramp), 0},
> +};
> +
> +/* SRC */
> +static const struct sof_topology_token src_tokens[] = {
> +	{SOF_TKN_SRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD,
> +		get_token_uint32_t,
> +		offsetof(struct sof_ipc_comp_src, source_rate), 0},
> +	{SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD,
> +		get_token_uint32_t,
> +		offsetof(struct sof_ipc_comp_src, sink_rate), 0},
> +};
> +
> +/* Tone */
> +static const struct sof_topology_token tone_tokens[] = {
> +};
> +
> +/* Generic components */
> +static const struct sof_topology_token comp_tokens[] = {
> +	{SOF_TKN_COMP_PERIOD_SINK_COUNT,
> +		SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_uint32_t,
> +		offsetof(struct sof_ipc_comp_config, periods_sink), 0},
> +	{SOF_TKN_COMP_PERIOD_SOURCE_COUNT,
> +		SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_uint32_t,
> +		offsetof(struct sof_ipc_comp_config, periods_source), 0},
> +	{SOF_TKN_COMP_FORMAT,
> +		SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format,
> +		offsetof(struct sof_ipc_comp_config, frame_fmt), 0},
> +	{SOF_TKN_COMP_PRELOAD_COUNT,
> +		SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_uint32_t,
> +		offsetof(struct sof_ipc_comp_config, preload_count), 0},
> +};
> +
> +int sof_parse_tokens(void *object,
> +			    const struct sof_topology_token *tokens,
> +			    int count,
> +			    struct snd_soc_tplg_vendor_array *array,
> +			    int priv_size);
> +void sof_parse_string_tokens(void *object,
> +			     const struct sof_topology_token *tokens,
> +			     int count,
> +			     struct snd_soc_tplg_vendor_array *array);
> +void sof_parse_uuid_tokens(void *object,
> +			   const struct sof_topology_token *tokens,
> +			   int count,
> +			   struct snd_soc_tplg_vendor_array *array);
> +void sof_parse_word_tokens(void *object,
> +			   const struct sof_topology_token *tokens,
> +			   int count,
> +			   struct snd_soc_tplg_vendor_array *array);
> +
> +int parse_topology(char *filename, struct sof *sof, int *fr_id, int *fw_id,
> +		   int *sched_id, char *bits_in,
> +		   char *in_file, char *out_file);
> +
> +#endif


More information about the Sound-open-firmware mailing list