[Sound-open-firmware] [PATCH 0/3] Host based component testing
This set of patches introduces the testbench for audio processing components in SOF. The testbench enables for functional/quality test of the components. The testbench reads in audio samples from a file and write out the processed samples to the output file.
The common directory includes routines for topology parsing and setting up sof ipc, scheduler and processing pipelines. It also includes simpler implementations for SOF features such as tracing. It also includes the fileread/filewrite components that allow reading in or writing out samples from/to a file.
The volume testbench directory includes the testbench for volume component. The volume testbench reads in input samples from a file ,schedules pipeline copy and copies the processed sample to the output file. The output file can be used to perform audio quality tests using matlab/octave. Testbench for other components such as src/eq will be added later.
Ranjani Sridharan (3): [RFC]tune: add common testbench common headers and source files [RFC]tune: add fileread and filewrite components [RFC]tune: add volume testbench
Makefile.am | 2 +- configure.ac | 5 +- 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 +++++++++++ tune/common/fileread.c | 385 ++++++++++++++++++++ tune/common/filewrite.c | 395 +++++++++++++++++++++ tune/volume/Makefile.am | 17 + tune/volume/volume_test.c | 160 +++++++++ 12 files changed, 2628 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 create mode 100644 tune/common/fileread.c create mode 100644 tune/common/filewrite.c create mode 100644 tune/volume/Makefile.am create mode 100644 tune/volume/volume_test.c
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.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@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@linux.intel.com + * Liam Girdwood liam.r.girdwood@linux.intel.com + * Keyon Jie yang.jie@linux.intel.com + * Ranjani Sridharan ranjani.sridharan@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; +} + +/* 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"; + } +} + +static char replace_blank(char x) +{ + 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); +} + +/* 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, ¶ms); + if (ret < 0) + printf("error: pipeline_params\n"); + + return ret; +} + +void debug_print(char *message) +{ +#ifdef DEBUG_PRINT + printf("debug: %s", message); +#endif +} 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@linux.intel.com + * Liam Girdwood liam.r.girdwood@linux.intel.com + * Keyon Jie yang.jie@linux.intel.com + * Ranjani Sridharan ranjani.sridharan@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@linux.intel.com + * Liam Girdwood liam.r.girdwood@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: + ; + /* 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@linux.intel.com + * Ranjani Sridharan ranjani.sridharan@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
On 03.05.2018 07:31, 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.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@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@linux.intel.com
Liam Girdwood <liam.r.girdwood@linux.intel.com>
Keyon Jie <yang.jie@linux.intel.com>
Ranjani Sridharan <ranjani.sridharan@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;
+}
+/* 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) +{ +}
Should this function include call to _trace_error(event) to see the event in trace?
+void _trace_event_mbox_atomic(uint32_t event) +{ +}
And this should call _trace_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";
- }
+}
+static char replace_blank(char x) +{
- 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);
- }
+}
Should we use printf(stderr, "") for traces? Then it could be simple to direct the trace output to file with "2> trace.txt" if desired. Normally stderr and stdout prints are not separated so there would be no impact if no redirect used.
+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);
- }
+}
The same
+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);
+}
+/* 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, ¶ms);
- if (ret < 0)
printf("error: pipeline_params\n");
- return ret;
+}
+void debug_print(char *message) +{ +#ifdef DEBUG_PRINT
printf("debug: %s", message);
+#endif +} 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@linux.intel.com
Liam Girdwood <liam.r.girdwood@linux.intel.com>
Keyon Jie <yang.jie@linux.intel.com>
Ranjani Sridharan <ranjani.sridharan@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@linux.intel.com
Liam Girdwood <liam.r.girdwood@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:
;
/* 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@linux.intel.com
Ranjani Sridharan <ranjani.sridharan@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
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@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@linux.intel.com
Liam Girdwood <liam.r.girdwood@linux.intel.com>
Keyon Jie <yang.jie@linux.intel.com>
Ranjani Sridharan <ranjani.sridharan@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, ¶ms);
- 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@linux.intel.com
Liam Girdwood <liam.r.girdwood@linux.intel.com>
Keyon Jie <yang.jie@linux.intel.com>
Ranjani Sridharan <ranjani.sridharan@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@linux.intel.com
Liam Girdwood <liam.r.girdwood@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@linux.intel.com
Ranjani Sridharan <ranjani.sridharan@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
This patch adds the fileread and filewrite components that testbench can liaise with to read in samples and write out processed samples.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com --- tune/common/fileread.c | 385 ++++++++++++++++++++++++++++++++++++++++++++++ tune/common/filewrite.c | 395 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 780 insertions(+) create mode 100644 tune/common/fileread.c create mode 100644 tune/common/filewrite.c
diff --git a/tune/common/fileread.c b/tune/common/fileread.c new file mode 100644 index 0000000..0435fcf --- /dev/null +++ b/tune/common/fileread.c @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2016, 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@linux.intel.com + * Ranjani Sridharan ranjani.sridharan@linux.intel.com + */ + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <errno.h> +#include <inttypes.h> +#include <sof/sof.h> +#include <sof/lock.h> +#include <sof/list.h> +#include <sof/stream.h> +#include <sof/work.h> +#include <sof/clock.h> +#include <sof/audio/component.h> +#include <sof/audio/format.h> +#include <sof/audio/pipeline.h> +#include <uapi/ipc.h> +#include "common_test.h" + +#define SHRINK_SINK 0 + +static inline void circular_inc_wrap(int32_t **ptr, int32_t *end, size_t size) +{ + if (*ptr >= end) + *ptr = (int32_t *) ((size_t)*ptr - size); +} + +static inline void circular_inc_wrap_16(int16_t **ptr, int16_t *end, + size_t size) +{ + if (*ptr >= end) + *ptr = (int16_t *) ((size_t)*ptr - size); +} + +static int fileread_s32_default(struct comp_dev *dev, struct comp_buffer *sink, + struct comp_buffer *source, uint32_t frames) +{ + struct fileread_comp_data *cd = comp_get_drvdata(dev); + int32_t *dest = (int32_t *) sink->w_ptr; + int i, n, n_wrap_dest, n_min, ret; + int nch = dev->params.channels; + int nread = 0; + + n = frames * nch; + while (n > 0) { + n_wrap_dest = (int32_t *) sink->end_addr - dest; + n_min = (n < n_wrap_dest) ? n : n_wrap_dest; + while (n_min > 0) { + n -= nch; + n_min -= nch; + for (i = 0; i < nch; i++) { + ret = fscanf(cd->frs.fh, "%d", dest); + nread++; + dest++; + if (ret == EOF) + goto read_eof; + } + } + circular_inc_wrap(&dest, sink->end_addr, sink->size); + } + + cd->frs.n += nread; + return nread; + +read_eof: + cd->frs.n += nread; + cd->frs.reached_eof = 1; + return nread; +} + +static int fileread_s16(struct comp_dev *dev, struct comp_buffer *sink, + struct comp_buffer *source, uint32_t frames) +{ + struct fileread_comp_data *cd = comp_get_drvdata(dev); + int16_t *dest = (int16_t *) sink->w_ptr; + int i, n, n_wrap_dest, n_min, ret; + int nch = dev->params.channels; + int nread = 0; + + n = frames * nch; + while (n > 0) { + n_wrap_dest = (int16_t *) sink->end_addr - dest; + n_min = (n < n_wrap_dest) ? n : n_wrap_dest; + while (n_min > 0) { + n -= nch; + n_min -= nch; + for (i = 0; i < nch; i++) { + ret = fscanf(cd->frs.fh, "%hd", dest); + nread++; + dest++; + if (ret == EOF) + goto read_eof; + } + } + circular_inc_wrap_16(&dest, sink->end_addr, sink->size); + } + + cd->frs.n += nread; + return nread; + +read_eof: + cd->frs.n += nread; + cd->frs.reached_eof = 1; + return nread; +} + +static int fileread_s24(struct comp_dev *dev, struct comp_buffer *sink, + struct comp_buffer *source, uint32_t frames) +{ + struct fileread_comp_data *cd = comp_get_drvdata(dev); + int32_t *dest = (int32_t *) sink->w_ptr; + int32_t sample; + int i, n, n_wrap_dest, n_min, ret; + int nch = dev->params.channels; + int nread = 0; + + n = frames * nch; + while (n > 0) { + n_wrap_dest = (int32_t *) sink->end_addr - dest; + n_min = (n < n_wrap_dest) ? n : n_wrap_dest; + while (n_min > 0) { + n -= nch; + n_min -= nch; + for (i = 0; i < nch; i++) { + ret = fscanf(cd->frs.fh, "%d", &sample); + *dest = sample & 0x00ffffff; /* Mask bits */ + nread++; + dest++; + if (ret == EOF) + goto read_eof; + } + } + circular_inc_wrap(&dest, sink->end_addr, sink->size); + } + + cd->frs.n += nread; + return nread; + +read_eof: + cd->frs.n += nread; + cd->frs.reached_eof = 1; + return nread; +} + +static struct comp_dev *fileread_new(struct sof_ipc_comp *comp) +{ + struct comp_dev *dev; + struct sof_ipc_comp_fileread *fileread; + struct sof_ipc_comp_fileread *ipc_fileread + = (struct sof_ipc_comp_fileread *) comp; + struct fileread_comp_data *cd; + + dev = malloc(COMP_SIZE(struct sof_ipc_comp_fileread)); + if (dev == NULL) + return NULL; + + fileread = (struct sof_ipc_comp_fileread *) &dev->comp; + memcpy(fileread, ipc_fileread, sizeof(struct sof_ipc_comp_fileread)); + + cd = malloc(sizeof(*cd)); + if (cd == NULL) { + free(dev); + return NULL; + } + + comp_set_drvdata(dev, cd); + cd->fileread_func = fileread_s32_default; + + /* Get filename from IPC and open file */ + strcpy(cd->frs.fn, ipc_fileread->fn); + cd->frs.fh = fopen(cd->frs.fn, "r"); + if (cd->frs.fh == NULL) { + fprintf(stderr, "Error: File %s open for read failed.\n", + cd->frs.fn); + free(cd); + free(dev); + return NULL; + } + cd->frs.reached_eof = 0; + cd->frs.n = 0; + + return dev; +} + +static void fileread_free(struct comp_dev *dev) +{ + struct fileread_data *td = comp_get_drvdata(dev); + struct fileread_comp_data *cd = comp_get_drvdata(dev); + + fclose(cd->frs.fh); + free(td); + free(dev); +} + +/* set component audio stream parameters */ +static int fileread_params(struct comp_dev *dev) +{ + struct fileread_comp_data *cd = comp_get_drvdata(dev); + struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev); + + /* Need to compute this in non-host endpoint */ + dev->frame_bytes = + dev->params.sample_container_bytes * dev->params.channels; + + /* calculate period size based on config */ + cd->period_bytes = dev->frames * dev->frame_bytes; + + /* File to sink supports only S32_LE/S16_LE/S24_4LE PCM formats */ + if ((config->frame_fmt != SOF_IPC_FRAME_S32_LE) + && (config->frame_fmt != SOF_IPC_FRAME_S24_4LE) + && (config->frame_fmt != SOF_IPC_FRAME_S16_LE)) + return -EINVAL; + + return 0; +} + +static int fr_cmd(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) +{ + return -EINVAL; +} + +static int fileread_trigger(struct comp_dev *dev, int cmd) +{ + return comp_set_state(dev, cmd); +} + +/* used to pass standard and bespoke commands (with data) to component */ +static int fileread_cmd(struct comp_dev *dev, int cmd, void *data) +{ + struct sof_ipc_ctrl_data *cdata = data; + int ret = 0; + + switch (cmd) { + case COMP_CMD_SET_DATA: + ret = fr_cmd(dev, cdata); + break; + default: + break; + } + + return ret; +} + +/* copy and process stream data from source to sink buffers + * returns the number of bytes copied + */ +static int fileread_copy(struct comp_dev *dev) +{ + struct comp_buffer *sink; + struct comp_buffer *source = NULL; + struct fileread_comp_data *cd = comp_get_drvdata(dev); + int ret = 0; + + /* fileread component sink buffer */ + sink = list_first_item(&dev->bsink_list, struct comp_buffer, + source_list); + + /* Test that sink has enough free frames */ + if (sink->free >= cd->period_bytes && !cd->frs.reached_eof) { + /* Read PCM samples from file */ + ret = cd->fileread_func(dev, sink, source, dev->frames); + if (ret > 0) + comp_update_buffer_produce(sink, ret * + dev->params.sample_container_bytes); + } + + return ret; +} + +static int fileread_prepare(struct comp_dev *dev) +{ + struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev); + struct comp_buffer *sink_buffer; + struct fileread_comp_data *cd = comp_get_drvdata(dev); + int ret = 0; + + /* fileread component sink buffer */ + sink_buffer = list_first_item(&dev->bsink_list, struct comp_buffer, + source_list); + + switch (config->frame_fmt) { + case(SOF_IPC_FRAME_S16_LE): +#if SHRINK_SINK == 1 + /* set downstream buffer size */ + ret = buffer_set_size(sink_buffer, dev->frames * 2 * + config->periods_sink * dev->params.channels); + if (ret < 0) { + printf("error: fileread buffer size set\n"); + return ret; + } +#endif + buffer_reset_pos(sink_buffer); + + /* set fileread function */ + cd->fileread_func = fileread_s16; + break; + case(SOF_IPC_FRAME_S24_4LE): +#if SHRINK_SINK == 1 + /* set downstream buffer size */ + ret = buffer_set_size(sink_buffer, dev->frames * 4 * + config->periods_sink * dev->params.channels); + if (ret < 0) { + printf("error: fileread buffer size set\n"); + return ret; + } +#endif + buffer_reset_pos(sink_buffer); + /* set fileread function */ + cd->fileread_func = fileread_s24; + break; + case(SOF_IPC_FRAME_S32_LE): +#if SHRINK_SINK == 1 + /* set downstream buffer size */ + ret = buffer_set_size(sink_buffer, dev->frames * 4 * + config->periods_sink * dev->params.channels); + if (ret < 0) { + printf("error: fileread buffer size set\n"); + return ret; + } +#endif + buffer_reset_pos(sink_buffer); + break; + default: + return -EINVAL; + } + + dev->state = COMP_STATE_PREPARE; + + return ret; +} + +static int fileread_reset(struct comp_dev *dev) +{ + dev->state = COMP_STATE_INIT; + + return 0; +} + +struct comp_driver comp_fileread = { + .type = SOF_COMP_FILEREAD, + .ops = { + .new = fileread_new, + .free = fileread_free, + .params = fileread_params, + .cmd = fileread_cmd, + .trigger = fileread_trigger, + .copy = fileread_copy, + .prepare = fileread_prepare, + .reset = fileread_reset, + }, +}; + +void sys_comp_fileread_init(void) +{ + comp_register(&comp_fileread); +} diff --git a/tune/common/filewrite.c b/tune/common/filewrite.c new file mode 100644 index 0000000..fc8d76e --- /dev/null +++ b/tune/common/filewrite.c @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2016, 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@linux.intel.com + * Ranjani Sridharan ranjani.sridharan@linux.intel.com + */ + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <errno.h> +#include <sof/sof.h> +#include <sof/lock.h> +#include <sof/list.h> +#include <sof/stream.h> +#include <sof/work.h> +#include <sof/clock.h> +#include <sof/audio/component.h> +#include <sof/audio/format.h> +#include <sof/audio/pipeline.h> +#include "common_test.h" + +static inline void circular_inc_wrap(int32_t **ptr, int32_t *end, + size_t size) +{ + if (*ptr >= end) + *ptr = (int32_t *) ((size_t)*ptr - size); +} + +static inline void circular_inc_wrap_16(int16_t **ptr, int16_t *end, + size_t size) +{ + if (*ptr >= end) + *ptr = (int16_t *) ((size_t)*ptr - size); +} + +static int filewrite_s32_default(struct comp_dev *dev, + struct comp_buffer *sink, + struct comp_buffer *source, + uint32_t frames) +{ + struct filewrite_comp_data *cd = comp_get_drvdata(dev); + int32_t *src = (int32_t *) source->r_ptr; + int i, n, n_wrap_src, n_min, ret; + int nch = dev->params.channels; + int nwrite = 0; + + if (cd->fws.write_fail) + return -EINVAL; + + n = frames * nch; + + while (n > 0) { + n_wrap_src = (int32_t *) source->end_addr - src; + n_min = (n < n_wrap_src) ? n : n_wrap_src; + while (n_min > 0) { + n -= nch; + n_min -= nch; + for (i = 0; i < nch; i++) { + ret = fprintf(cd->fws.fh, "%d\n", *src); + nwrite++; + src++; + if (ret < 0) { + cd->fws.write_fail = 1; + goto quit; + } + } + } + circular_inc_wrap(&src, source->end_addr, source->size); + } + +quit: + cd->fws.n += nwrite; + return nwrite; +} + +static int filewrite_s16(struct comp_dev *dev, struct comp_buffer *sink, + struct comp_buffer *source, uint32_t frames) +{ + struct filewrite_comp_data *cd = comp_get_drvdata(dev); + int16_t *src = (int16_t *) source->r_ptr; + int i, n, n_wrap_src, n_min, ret; + int nch = dev->params.channels; + int nwrite = 0; + + if (cd->fws.write_fail) + return -EINVAL; + + n = frames * nch; + + while (n > 0) { + n_wrap_src = (int16_t *) source->end_addr - src; + n_min = (n < n_wrap_src) ? n : n_wrap_src; + while (n_min > 0) { + n -= nch; + n_min -= nch; + for (i = 0; i < nch; i++) { + ret = fprintf(cd->fws.fh, "%d\n", *src); + nwrite++; + src++; + if (ret < 0) { + cd->fws.write_fail = 1; + goto quit; + } + } + } + circular_inc_wrap_16(&src, source->end_addr, source->size); + } + + +quit: + cd->fws.n += nwrite; + return nwrite; +} + +static int filewrite_s24(struct comp_dev *dev, + struct comp_buffer *sink, + struct comp_buffer *source, + uint32_t frames) +{ + struct filewrite_comp_data *cd = comp_get_drvdata(dev); + int32_t *src = (int32_t *) source->r_ptr; + int32_t se; + int i, n, n_wrap_src, n_min, ret; + int nch = dev->params.channels; + int nwrite = 0; + + if (cd->fws.write_fail) + return -EINVAL; + + n = frames * nch; + + while (n > 0) { + n_wrap_src = (int32_t *) source->end_addr - src; + n_min = (n < n_wrap_src) ? n : n_wrap_src; + while (n_min > 0) { + n -= nch; + n_min -= nch; + for (i = 0; i < nch; i++) { + se = *src << 8; + ret = fprintf(cd->fws.fh, "%d\n", se >> 8); + nwrite++; + src++; + if (ret < 0) { + cd->fws.write_fail = 1; + goto quit; + } + } + } + circular_inc_wrap(&src, source->end_addr, source->size); + } + + +quit: + cd->fws.n += nwrite; + return nwrite; +} + +static struct comp_dev *filewrite_new(struct sof_ipc_comp *comp) +{ + struct comp_dev *dev; + struct sof_ipc_comp_filewrite *filewrite; + struct sof_ipc_comp_filewrite *ipc_filewrite + = (struct sof_ipc_comp_filewrite *) comp; + struct filewrite_comp_data *cd; + + dev = malloc(COMP_SIZE(struct sof_ipc_comp_filewrite)); + if (dev == NULL) + return NULL; + + filewrite = (struct sof_ipc_comp_filewrite *) &dev->comp; + memcpy(filewrite, ipc_filewrite, sizeof(struct sof_ipc_comp_filewrite)); + + cd = malloc(sizeof(*cd)); + if (cd == NULL) { + free(dev); + return NULL; + } + + comp_set_drvdata(dev, cd); + cd->filewrite_func = filewrite_s32_default; + + strncpy(cd->fws.fn, ipc_filewrite->fn, FILEWRITE_FN_MAXLENGTH); + cd->fws.fh = fopen(cd->fws.fn, "w"); + if (cd->fws.fh == NULL) { + fprintf(stderr, "Error: File %s open for write failed.\n", + cd->fws.fn); + free(cd); + free(dev); + return NULL; + } + + cd->fws.write_fail = 0; + cd->fws.n = 0; + return dev; +} + +static void filewrite_free(struct comp_dev *dev) +{ + struct filewrite_data *td = comp_get_drvdata(dev); + struct filewrite_comp_data *cd = comp_get_drvdata(dev); + + fclose(cd->fws.fh); + free(td); + free(dev); +} + +/* set component audio stream parameters */ +static int filewrite_params(struct comp_dev *dev) +{ + struct filewrite_comp_data *cd = comp_get_drvdata(dev); + struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev); + + /* Need to compute this in non-dai endpoint */ + dev->frame_bytes = + dev->params.sample_container_bytes * dev->params.channels; + + /* calculate period size based on config */ + cd->period_bytes = dev->frames * dev->frame_bytes; + + /* File to sink supports only S32_LE/S16_LE/S24_4LE PCM formats */ + if ((config->frame_fmt != SOF_IPC_FRAME_S32_LE) + && (config->frame_fmt != SOF_IPC_FRAME_S24_4LE) + && (config->frame_fmt != SOF_IPC_FRAME_S16_LE)) + return -EINVAL; + + return 0; +} + +static int fw_cmd(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) +{ + return -EINVAL; +} + +static int filewrite_trigger(struct comp_dev *dev, int cmd) +{ + return comp_set_state(dev, cmd); +} + +/* used to pass standard and bespoke commands (with data) to component */ +static int filewrite_cmd(struct comp_dev *dev, int cmd, void *data) +{ + struct sof_ipc_ctrl_data *cdata = data; + int ret = 0; + + switch (cmd) { + case COMP_CMD_SET_DATA: + ret = fw_cmd(dev, cdata); + break; + default: + break; + } + + return ret; +} + +/* copy and process stream data from source to sink buffers + * returns the number of bytes copied + */ +static int filewrite_copy(struct comp_dev *dev) +{ + struct comp_buffer *sink = NULL; + struct comp_buffer *source; + struct filewrite_comp_data *cd = comp_get_drvdata(dev); + int ret = 0; + + /* Get filewrite component source buffer */ + source = list_first_item(&dev->bsource_list, struct comp_buffer, + sink_list); + + /* Test that source has enough free frames */ + if (source->avail >= cd->period_bytes) { + /* Write PCM samples into file */ + ret = cd->filewrite_func(dev, sink, source, dev->frames); + if (ret < 0) { + printf("error: filewrite fail\n"); + return ret; + } + comp_update_buffer_consume(source, ret * + dev->params.sample_container_bytes); + } + + return ret; +} + +static int filewrite_prepare(struct comp_dev *dev) +{ + struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev); + struct comp_buffer *source_buffer; + struct filewrite_comp_data *cd = comp_get_drvdata(dev); + int ret = 0; + + /* fileread component sink buffer */ + source_buffer = list_first_item(&dev->bsource_list, struct comp_buffer, + sink_list); + + switch (config->frame_fmt) { + case(SOF_IPC_FRAME_S16_LE): + /* set downstream buffer size */ + ret = buffer_set_size(source_buffer, dev->frames * 2 * + config->periods_source * dev->params.channels); + if (ret < 0) { + printf("error: fileread buffer size set\n"); + return ret; + } + + buffer_reset_pos(source_buffer); + + /* set fileread function */ + cd->filewrite_func = filewrite_s16; + break; + case(SOF_IPC_FRAME_S24_4LE): + /* set downstream buffer size */ + ret = buffer_set_size(source_buffer, dev->frames * 4 * + config->periods_source * + dev->params.channels); + if (ret < 0) { + printf("error: fileread buffer size set\n"); + return ret; + } + + buffer_reset_pos(source_buffer); + + /* set fileread function */ + cd->filewrite_func = filewrite_s24; + break; + case(SOF_IPC_FRAME_S32_LE): + /* set downstream buffer size */ + ret = buffer_set_size(source_buffer, dev->frames * 4 * + config->periods_source * + dev->params.channels); + if (ret < 0) { + printf("error: fileread buffer size set\n"); + return ret; + } + + buffer_reset_pos(source_buffer); + break; + default: + return -EINVAL; + } + dev->state = COMP_STATE_PREPARE; + + return 0; +} + +static int filewrite_reset(struct comp_dev *dev) +{ + dev->state = COMP_STATE_INIT; + + return 0; +} + +struct comp_driver comp_filewrite = { + .type = SOF_COMP_FILEWRITE, + .ops = { + .new = filewrite_new, + .free = filewrite_free, + .params = filewrite_params, + .cmd = filewrite_cmd, + .trigger = filewrite_trigger, + .copy = filewrite_copy, + .prepare = filewrite_prepare, + .reset = filewrite_reset, + }, +}; + +void sys_comp_filewrite_init(void) +{ + comp_register(&comp_filewrite); +}
Hi,
A feature proposal below:
Thanks, Seppo
On 03.05.2018 07:31, Ranjani Sridharan wrote:
This patch adds the fileread and filewrite components that testbench can liaise with to read in samples and write out processed samples.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com
tune/common/fileread.c | 385 ++++++++++++++++++++++++++++++++++++++++++++++ tune/common/filewrite.c | 395 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 780 insertions(+) create mode 100644 tune/common/fileread.c create mode 100644 tune/common/filewrite.c
diff --git a/tune/common/fileread.c b/tune/common/fileread.c new file mode 100644 index 0000000..0435fcf --- /dev/null +++ b/tune/common/fileread.c @@ -0,0 +1,385 @@ +/*
- Copyright (c) 2016, 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@linux.intel.com
Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
- */
+#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <errno.h> +#include <inttypes.h> +#include <sof/sof.h> +#include <sof/lock.h> +#include <sof/list.h> +#include <sof/stream.h> +#include <sof/work.h> +#include <sof/clock.h> +#include <sof/audio/component.h> +#include <sof/audio/format.h> +#include <sof/audio/pipeline.h> +#include <uapi/ipc.h> +#include "common_test.h"
+#define SHRINK_SINK 0
+static inline void circular_inc_wrap(int32_t **ptr, int32_t *end, size_t size) +{
- if (*ptr >= end)
*ptr = (int32_t *) ((size_t)*ptr - size);
+}
+static inline void circular_inc_wrap_16(int16_t **ptr, int16_t *end,
size_t size)
+{
- if (*ptr >= end)
*ptr = (int16_t *) ((size_t)*ptr - size);
+}
+static int fileread_s32_default(struct comp_dev *dev, struct comp_buffer *sink,
struct comp_buffer *source, uint32_t frames)
+{
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- int32_t *dest = (int32_t *) sink->w_ptr;
- int i, n, n_wrap_dest, n_min, ret;
- int nch = dev->params.channels;
- int nread = 0;
- n = frames * nch;
- while (n > 0) {
n_wrap_dest = (int32_t *) sink->end_addr - dest;
n_min = (n < n_wrap_dest) ? n : n_wrap_dest;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
ret = fscanf(cd->frs.fh, "%d", dest);
nread++;
dest++;
if (ret == EOF)
goto read_eof;
}
}
circular_inc_wrap(&dest, sink->end_addr, sink->size);
- }
- cd->frs.n += nread;
- return nread;
+read_eof:
- cd->frs.n += nread;
- cd->frs.reached_eof = 1;
- return nread;
+}
The text format for file data is nice as human readable format and possibility to manually create test vectors with a text editor but could there be in addition similar functions to read/write raw binary files with fread()/fwrite() and invoke such file mode via command line if desired? Then it would be possible to convert wav or mp3 etc. to compatible raw binary format easily with sox and back.
Binary format would be also faster and more compact for CI tests.
+static int fileread_s16(struct comp_dev *dev, struct comp_buffer *sink,
- struct comp_buffer *source, uint32_t frames)
+{
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- int16_t *dest = (int16_t *) sink->w_ptr;
- int i, n, n_wrap_dest, n_min, ret;
- int nch = dev->params.channels;
- int nread = 0;
- n = frames * nch;
- while (n > 0) {
n_wrap_dest = (int16_t *) sink->end_addr - dest;
n_min = (n < n_wrap_dest) ? n : n_wrap_dest;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
ret = fscanf(cd->frs.fh, "%hd", dest);
nread++;
dest++;
if (ret == EOF)
goto read_eof;
}
}
circular_inc_wrap_16(&dest, sink->end_addr, sink->size);
- }
- cd->frs.n += nread;
- return nread;
+read_eof:
- cd->frs.n += nread;
- cd->frs.reached_eof = 1;
- return nread;
+}
+static int fileread_s24(struct comp_dev *dev, struct comp_buffer *sink,
- struct comp_buffer *source, uint32_t frames)
+{
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- int32_t *dest = (int32_t *) sink->w_ptr;
- int32_t sample;
- int i, n, n_wrap_dest, n_min, ret;
- int nch = dev->params.channels;
- int nread = 0;
- n = frames * nch;
- while (n > 0) {
n_wrap_dest = (int32_t *) sink->end_addr - dest;
n_min = (n < n_wrap_dest) ? n : n_wrap_dest;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
ret = fscanf(cd->frs.fh, "%d", &sample);
*dest = sample & 0x00ffffff; /* Mask bits */
nread++;
dest++;
if (ret == EOF)
goto read_eof;
}
}
circular_inc_wrap(&dest, sink->end_addr, sink->size);
- }
- cd->frs.n += nread;
- return nread;
+read_eof:
- cd->frs.n += nread;
- cd->frs.reached_eof = 1;
- return nread;
+}
+static struct comp_dev *fileread_new(struct sof_ipc_comp *comp) +{
- struct comp_dev *dev;
- struct sof_ipc_comp_fileread *fileread;
- struct sof_ipc_comp_fileread *ipc_fileread
= (struct sof_ipc_comp_fileread *) comp;
- struct fileread_comp_data *cd;
- dev = malloc(COMP_SIZE(struct sof_ipc_comp_fileread));
- if (dev == NULL)
return NULL;
- fileread = (struct sof_ipc_comp_fileread *) &dev->comp;
- memcpy(fileread, ipc_fileread, sizeof(struct sof_ipc_comp_fileread));
- cd = malloc(sizeof(*cd));
- if (cd == NULL) {
free(dev);
return NULL;
- }
- comp_set_drvdata(dev, cd);
- cd->fileread_func = fileread_s32_default;
- /* Get filename from IPC and open file */
- strcpy(cd->frs.fn, ipc_fileread->fn);
- cd->frs.fh = fopen(cd->frs.fn, "r");
- if (cd->frs.fh == NULL) {
fprintf(stderr, "Error: File %s open for read failed.\n",
cd->frs.fn);
free(cd);
free(dev);
return NULL;
- }
- cd->frs.reached_eof = 0;
- cd->frs.n = 0;
- return dev;
+}
+static void fileread_free(struct comp_dev *dev) +{
- struct fileread_data *td = comp_get_drvdata(dev);
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- fclose(cd->frs.fh);
- free(td);
- free(dev);
+}
+/* set component audio stream parameters */ +static int fileread_params(struct comp_dev *dev) +{
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
- /* Need to compute this in non-host endpoint */
- dev->frame_bytes =
dev->params.sample_container_bytes * dev->params.channels;
- /* calculate period size based on config */
- cd->period_bytes = dev->frames * dev->frame_bytes;
- /* File to sink supports only S32_LE/S16_LE/S24_4LE PCM formats */
- if ((config->frame_fmt != SOF_IPC_FRAME_S32_LE)
&& (config->frame_fmt != SOF_IPC_FRAME_S24_4LE)
&& (config->frame_fmt != SOF_IPC_FRAME_S16_LE))
return -EINVAL;
- return 0;
+}
+static int fr_cmd(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) +{
- return -EINVAL;
+}
+static int fileread_trigger(struct comp_dev *dev, int cmd) +{
- return comp_set_state(dev, cmd);
+}
+/* used to pass standard and bespoke commands (with data) to component */ +static int fileread_cmd(struct comp_dev *dev, int cmd, void *data) +{
- struct sof_ipc_ctrl_data *cdata = data;
- int ret = 0;
- switch (cmd) {
- case COMP_CMD_SET_DATA:
ret = fr_cmd(dev, cdata);
break;
- default:
break;
- }
- return ret;
+}
+/* copy and process stream data from source to sink buffers
- returns the number of bytes copied
- */
+static int fileread_copy(struct comp_dev *dev) +{
- struct comp_buffer *sink;
- struct comp_buffer *source = NULL;
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- int ret = 0;
- /* fileread component sink buffer */
- sink = list_first_item(&dev->bsink_list, struct comp_buffer,
source_list);
- /* Test that sink has enough free frames */
- if (sink->free >= cd->period_bytes && !cd->frs.reached_eof) {
/* Read PCM samples from file */
ret = cd->fileread_func(dev, sink, source, dev->frames);
if (ret > 0)
comp_update_buffer_produce(sink, ret *
dev->params.sample_container_bytes);
- }
- return ret;
+}
+static int fileread_prepare(struct comp_dev *dev) +{
- struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
- struct comp_buffer *sink_buffer;
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- int ret = 0;
- /* fileread component sink buffer */
- sink_buffer = list_first_item(&dev->bsink_list, struct comp_buffer,
source_list);
- switch (config->frame_fmt) {
- case(SOF_IPC_FRAME_S16_LE):
+#if SHRINK_SINK == 1
/* set downstream buffer size */
ret = buffer_set_size(sink_buffer, dev->frames * 2 *
config->periods_sink * dev->params.channels);
if (ret < 0) {
printf("error: fileread buffer size set\n");
return ret;
}
+#endif
buffer_reset_pos(sink_buffer);
/* set fileread function */
cd->fileread_func = fileread_s16;
break;
- case(SOF_IPC_FRAME_S24_4LE):
+#if SHRINK_SINK == 1
/* set downstream buffer size */
ret = buffer_set_size(sink_buffer, dev->frames * 4 *
config->periods_sink * dev->params.channels);
if (ret < 0) {
printf("error: fileread buffer size set\n");
return ret;
}
+#endif
buffer_reset_pos(sink_buffer);
/* set fileread function */
cd->fileread_func = fileread_s24;
break;
- case(SOF_IPC_FRAME_S32_LE):
+#if SHRINK_SINK == 1
/* set downstream buffer size */
ret = buffer_set_size(sink_buffer, dev->frames * 4 *
config->periods_sink * dev->params.channels);
if (ret < 0) {
printf("error: fileread buffer size set\n");
return ret;
}
+#endif
buffer_reset_pos(sink_buffer);
break;
- default:
return -EINVAL;
- }
- dev->state = COMP_STATE_PREPARE;
- return ret;
+}
+static int fileread_reset(struct comp_dev *dev) +{
- dev->state = COMP_STATE_INIT;
- return 0;
+}
+struct comp_driver comp_fileread = {
- .type = SOF_COMP_FILEREAD,
- .ops = {
.new = fileread_new,
.free = fileread_free,
.params = fileread_params,
.cmd = fileread_cmd,
.trigger = fileread_trigger,
.copy = fileread_copy,
.prepare = fileread_prepare,
.reset = fileread_reset,
- },
+};
+void sys_comp_fileread_init(void) +{
- comp_register(&comp_fileread);
+} diff --git a/tune/common/filewrite.c b/tune/common/filewrite.c new file mode 100644 index 0000000..fc8d76e --- /dev/null +++ b/tune/common/filewrite.c @@ -0,0 +1,395 @@ +/*
- Copyright (c) 2016, 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@linux.intel.com
Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
- */
+#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <errno.h> +#include <sof/sof.h> +#include <sof/lock.h> +#include <sof/list.h> +#include <sof/stream.h> +#include <sof/work.h> +#include <sof/clock.h> +#include <sof/audio/component.h> +#include <sof/audio/format.h> +#include <sof/audio/pipeline.h> +#include "common_test.h"
+static inline void circular_inc_wrap(int32_t **ptr, int32_t *end,
size_t size)
+{
- if (*ptr >= end)
*ptr = (int32_t *) ((size_t)*ptr - size);
+}
+static inline void circular_inc_wrap_16(int16_t **ptr, int16_t *end,
size_t size)
+{
- if (*ptr >= end)
*ptr = (int16_t *) ((size_t)*ptr - size);
+}
+static int filewrite_s32_default(struct comp_dev *dev,
struct comp_buffer *sink,
struct comp_buffer *source,
uint32_t frames)
+{
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- int32_t *src = (int32_t *) source->r_ptr;
- int i, n, n_wrap_src, n_min, ret;
- int nch = dev->params.channels;
- int nwrite = 0;
- if (cd->fws.write_fail)
return -EINVAL;
- n = frames * nch;
- while (n > 0) {
n_wrap_src = (int32_t *) source->end_addr - src;
n_min = (n < n_wrap_src) ? n : n_wrap_src;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
ret = fprintf(cd->fws.fh, "%d\n", *src);
nwrite++;
src++;
if (ret < 0) {
cd->fws.write_fail = 1;
goto quit;
}
}
}
circular_inc_wrap(&src, source->end_addr, source->size);
- }
+quit:
- cd->fws.n += nwrite;
- return nwrite;
+}
+static int filewrite_s16(struct comp_dev *dev, struct comp_buffer *sink,
struct comp_buffer *source, uint32_t frames)
+{
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- int16_t *src = (int16_t *) source->r_ptr;
- int i, n, n_wrap_src, n_min, ret;
- int nch = dev->params.channels;
- int nwrite = 0;
- if (cd->fws.write_fail)
return -EINVAL;
- n = frames * nch;
- while (n > 0) {
n_wrap_src = (int16_t *) source->end_addr - src;
n_min = (n < n_wrap_src) ? n : n_wrap_src;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
ret = fprintf(cd->fws.fh, "%d\n", *src);
nwrite++;
src++;
if (ret < 0) {
cd->fws.write_fail = 1;
goto quit;
}
}
}
circular_inc_wrap_16(&src, source->end_addr, source->size);
- }
+quit:
- cd->fws.n += nwrite;
- return nwrite;
+}
+static int filewrite_s24(struct comp_dev *dev,
struct comp_buffer *sink,
struct comp_buffer *source,
uint32_t frames)
+{
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- int32_t *src = (int32_t *) source->r_ptr;
- int32_t se;
- int i, n, n_wrap_src, n_min, ret;
- int nch = dev->params.channels;
- int nwrite = 0;
- if (cd->fws.write_fail)
return -EINVAL;
- n = frames * nch;
- while (n > 0) {
n_wrap_src = (int32_t *) source->end_addr - src;
n_min = (n < n_wrap_src) ? n : n_wrap_src;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
se = *src << 8;
ret = fprintf(cd->fws.fh, "%d\n", se >> 8);
nwrite++;
src++;
if (ret < 0) {
cd->fws.write_fail = 1;
goto quit;
}
}
}
circular_inc_wrap(&src, source->end_addr, source->size);
- }
+quit:
- cd->fws.n += nwrite;
- return nwrite;
+}
+static struct comp_dev *filewrite_new(struct sof_ipc_comp *comp) +{
- struct comp_dev *dev;
- struct sof_ipc_comp_filewrite *filewrite;
- struct sof_ipc_comp_filewrite *ipc_filewrite
= (struct sof_ipc_comp_filewrite *) comp;
- struct filewrite_comp_data *cd;
- dev = malloc(COMP_SIZE(struct sof_ipc_comp_filewrite));
- if (dev == NULL)
return NULL;
- filewrite = (struct sof_ipc_comp_filewrite *) &dev->comp;
- memcpy(filewrite, ipc_filewrite, sizeof(struct sof_ipc_comp_filewrite));
- cd = malloc(sizeof(*cd));
- if (cd == NULL) {
free(dev);
return NULL;
- }
- comp_set_drvdata(dev, cd);
- cd->filewrite_func = filewrite_s32_default;
- strncpy(cd->fws.fn, ipc_filewrite->fn, FILEWRITE_FN_MAXLENGTH);
- cd->fws.fh = fopen(cd->fws.fn, "w");
- if (cd->fws.fh == NULL) {
fprintf(stderr, "Error: File %s open for write failed.\n",
cd->fws.fn);
free(cd);
free(dev);
return NULL;
- }
- cd->fws.write_fail = 0;
- cd->fws.n = 0;
- return dev;
+}
+static void filewrite_free(struct comp_dev *dev) +{
- struct filewrite_data *td = comp_get_drvdata(dev);
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- fclose(cd->fws.fh);
- free(td);
- free(dev);
+}
+/* set component audio stream parameters */ +static int filewrite_params(struct comp_dev *dev) +{
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
- /* Need to compute this in non-dai endpoint */
- dev->frame_bytes =
dev->params.sample_container_bytes * dev->params.channels;
- /* calculate period size based on config */
- cd->period_bytes = dev->frames * dev->frame_bytes;
- /* File to sink supports only S32_LE/S16_LE/S24_4LE PCM formats */
- if ((config->frame_fmt != SOF_IPC_FRAME_S32_LE)
&& (config->frame_fmt != SOF_IPC_FRAME_S24_4LE)
&& (config->frame_fmt != SOF_IPC_FRAME_S16_LE))
return -EINVAL;
- return 0;
+}
+static int fw_cmd(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) +{
- return -EINVAL;
+}
+static int filewrite_trigger(struct comp_dev *dev, int cmd) +{
- return comp_set_state(dev, cmd);
+}
+/* used to pass standard and bespoke commands (with data) to component */ +static int filewrite_cmd(struct comp_dev *dev, int cmd, void *data) +{
- struct sof_ipc_ctrl_data *cdata = data;
- int ret = 0;
- switch (cmd) {
- case COMP_CMD_SET_DATA:
ret = fw_cmd(dev, cdata);
break;
- default:
break;
- }
- return ret;
+}
+/* copy and process stream data from source to sink buffers
- returns the number of bytes copied
- */
+static int filewrite_copy(struct comp_dev *dev) +{
- struct comp_buffer *sink = NULL;
- struct comp_buffer *source;
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- int ret = 0;
- /* Get filewrite component source buffer */
- source = list_first_item(&dev->bsource_list, struct comp_buffer,
sink_list);
- /* Test that source has enough free frames */
- if (source->avail >= cd->period_bytes) {
/* Write PCM samples into file */
ret = cd->filewrite_func(dev, sink, source, dev->frames);
if (ret < 0) {
printf("error: filewrite fail\n");
return ret;
}
comp_update_buffer_consume(source, ret *
dev->params.sample_container_bytes);
- }
- return ret;
+}
+static int filewrite_prepare(struct comp_dev *dev) +{
- struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
- struct comp_buffer *source_buffer;
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- int ret = 0;
- /* fileread component sink buffer */
- source_buffer = list_first_item(&dev->bsource_list, struct comp_buffer,
sink_list);
- switch (config->frame_fmt) {
- case(SOF_IPC_FRAME_S16_LE):
/* set downstream buffer size */
ret = buffer_set_size(source_buffer, dev->frames * 2 *
config->periods_source * dev->params.channels);
if (ret < 0) {
printf("error: fileread buffer size set\n");
return ret;
}
buffer_reset_pos(source_buffer);
/* set fileread function */
cd->filewrite_func = filewrite_s16;
break;
- case(SOF_IPC_FRAME_S24_4LE):
/* set downstream buffer size */
ret = buffer_set_size(source_buffer, dev->frames * 4 *
config->periods_source *
dev->params.channels);
if (ret < 0) {
printf("error: fileread buffer size set\n");
return ret;
}
buffer_reset_pos(source_buffer);
/* set fileread function */
cd->filewrite_func = filewrite_s24;
break;
- case(SOF_IPC_FRAME_S32_LE):
/* set downstream buffer size */
ret = buffer_set_size(source_buffer, dev->frames * 4 *
config->periods_source *
dev->params.channels);
if (ret < 0) {
printf("error: fileread buffer size set\n");
return ret;
}
buffer_reset_pos(source_buffer);
break;
- default:
return -EINVAL;
- }
- dev->state = COMP_STATE_PREPARE;
- return 0;
+}
+static int filewrite_reset(struct comp_dev *dev) +{
- dev->state = COMP_STATE_INIT;
- return 0;
+}
+struct comp_driver comp_filewrite = {
- .type = SOF_COMP_FILEWRITE,
- .ops = {
.new = filewrite_new,
.free = filewrite_free,
.params = filewrite_params,
.cmd = filewrite_cmd,
.trigger = filewrite_trigger,
.copy = filewrite_copy,
.prepare = filewrite_prepare,
.reset = filewrite_reset,
- },
+};
+void sys_comp_filewrite_init(void) +{
- comp_register(&comp_filewrite);
+}
On Thu, 2018-05-03 at 10:42 +0300, Seppo Ingalsuo wrote:
Hi,
A feature proposal below:
Thanks, Seppo
On 03.05.2018 07:31, Ranjani Sridharan wrote:
This patch adds the fileread and filewrite components that testbench can liaise with to read in samples and write out processed samples.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com
tune/common/fileread.c | 385 ++++++++++++++++++++++++++++++++++++++++++++++ tune/common/filewrite.c | 395 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 780 insertions(+) create mode 100644 tune/common/fileread.c create mode 100644 tune/common/filewrite.c
diff --git a/tune/common/fileread.c b/tune/common/fileread.c new file mode 100644 index 0000000..0435fcf --- /dev/null +++ b/tune/common/fileread.c @@ -0,0 +1,385 @@ +/*
- Copyright (c) 2016, 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@linux.intel.com
Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
- */
+#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <errno.h> +#include <inttypes.h> +#include <sof/sof.h> +#include <sof/lock.h> +#include <sof/list.h> +#include <sof/stream.h> +#include <sof/work.h> +#include <sof/clock.h> +#include <sof/audio/component.h> +#include <sof/audio/format.h> +#include <sof/audio/pipeline.h> +#include <uapi/ipc.h> +#include "common_test.h"
+#define SHRINK_SINK 0
+static inline void circular_inc_wrap(int32_t **ptr, int32_t *end, size_t size) +{
- if (*ptr >= end)
*ptr = (int32_t *) ((size_t)*ptr - size);
+}
+static inline void circular_inc_wrap_16(int16_t **ptr, int16_t *end,
size_t size)
+{
- if (*ptr >= end)
*ptr = (int16_t *) ((size_t)*ptr - size);
+}
+static int fileread_s32_default(struct comp_dev *dev, struct comp_buffer *sink,
struct comp_buffer *source,
uint32_t frames) +{
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- int32_t *dest = (int32_t *) sink->w_ptr;
- int i, n, n_wrap_dest, n_min, ret;
- int nch = dev->params.channels;
- int nread = 0;
- n = frames * nch;
- while (n > 0) {
n_wrap_dest = (int32_t *) sink->end_addr - dest;
n_min = (n < n_wrap_dest) ? n : n_wrap_dest;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
ret = fscanf(cd->frs.fh, "%d",
dest);
nread++;
dest++;
if (ret == EOF)
goto read_eof;
}
}
circular_inc_wrap(&dest, sink->end_addr, sink-
size);
- }
- cd->frs.n += nread;
- return nread;
+read_eof:
- cd->frs.n += nread;
- cd->frs.reached_eof = 1;
- return nread;
+}
The text format for file data is nice as human readable format and possibility to manually create test vectors with a text editor but could there be in addition similar functions to read/write raw binary files with fread()/fwrite() and invoke such file mode via command line if desired? Then it would be possible to convert wav or mp3 etc. to compatible raw binary format easily with sox and back.
Binary format would be also faster and more compact for CI tests.
Sure, good idea. I will add support for binary input files too. Thanks!
+static int fileread_s16(struct comp_dev *dev, struct comp_buffer *sink,
- struct comp_buffer *source, uint32_t frames)
+{
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- int16_t *dest = (int16_t *) sink->w_ptr;
- int i, n, n_wrap_dest, n_min, ret;
- int nch = dev->params.channels;
- int nread = 0;
- n = frames * nch;
- while (n > 0) {
n_wrap_dest = (int16_t *) sink->end_addr - dest;
n_min = (n < n_wrap_dest) ? n : n_wrap_dest;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
ret = fscanf(cd->frs.fh, "%hd",
dest);
nread++;
dest++;
if (ret == EOF)
goto read_eof;
}
}
circular_inc_wrap_16(&dest, sink->end_addr, sink-
size);
- }
- cd->frs.n += nread;
- return nread;
+read_eof:
- cd->frs.n += nread;
- cd->frs.reached_eof = 1;
- return nread;
+}
+static int fileread_s24(struct comp_dev *dev, struct comp_buffer *sink,
- struct comp_buffer *source, uint32_t frames)
+{
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- int32_t *dest = (int32_t *) sink->w_ptr;
- int32_t sample;
- int i, n, n_wrap_dest, n_min, ret;
- int nch = dev->params.channels;
- int nread = 0;
- n = frames * nch;
- while (n > 0) {
n_wrap_dest = (int32_t *) sink->end_addr - dest;
n_min = (n < n_wrap_dest) ? n : n_wrap_dest;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
ret = fscanf(cd->frs.fh, "%d",
&sample);
*dest = sample & 0x00ffffff; /*
Mask bits */
nread++;
dest++;
if (ret == EOF)
goto read_eof;
}
}
circular_inc_wrap(&dest, sink->end_addr, sink-
size);
- }
- cd->frs.n += nread;
- return nread;
+read_eof:
- cd->frs.n += nread;
- cd->frs.reached_eof = 1;
- return nread;
+}
+static struct comp_dev *fileread_new(struct sof_ipc_comp *comp) +{
- struct comp_dev *dev;
- struct sof_ipc_comp_fileread *fileread;
- struct sof_ipc_comp_fileread *ipc_fileread
= (struct sof_ipc_comp_fileread *) comp;
- struct fileread_comp_data *cd;
- dev = malloc(COMP_SIZE(struct sof_ipc_comp_fileread));
- if (dev == NULL)
return NULL;
- fileread = (struct sof_ipc_comp_fileread *) &dev->comp;
- memcpy(fileread, ipc_fileread, sizeof(struct
sof_ipc_comp_fileread));
- cd = malloc(sizeof(*cd));
- if (cd == NULL) {
free(dev);
return NULL;
- }
- comp_set_drvdata(dev, cd);
- cd->fileread_func = fileread_s32_default;
- /* Get filename from IPC and open file */
- strcpy(cd->frs.fn, ipc_fileread->fn);
- cd->frs.fh = fopen(cd->frs.fn, "r");
- if (cd->frs.fh == NULL) {
fprintf(stderr, "Error: File %s open for read
failed.\n",
cd->frs.fn);
free(cd);
free(dev);
return NULL;
- }
- cd->frs.reached_eof = 0;
- cd->frs.n = 0;
- return dev;
+}
+static void fileread_free(struct comp_dev *dev) +{
- struct fileread_data *td = comp_get_drvdata(dev);
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- fclose(cd->frs.fh);
- free(td);
- free(dev);
+}
+/* set component audio stream parameters */ +static int fileread_params(struct comp_dev *dev) +{
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
- /* Need to compute this in non-host endpoint */
- dev->frame_bytes =
dev->params.sample_container_bytes * dev-
params.channels;
- /* calculate period size based on config */
- cd->period_bytes = dev->frames * dev->frame_bytes;
- /* File to sink supports only S32_LE/S16_LE/S24_4LE PCM
formats */
- if ((config->frame_fmt != SOF_IPC_FRAME_S32_LE)
&& (config->frame_fmt != SOF_IPC_FRAME_S24_4LE)
&& (config->frame_fmt != SOF_IPC_FRAME_S16_LE))
return -EINVAL;
- return 0;
+}
+static int fr_cmd(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) +{
- return -EINVAL;
+}
+static int fileread_trigger(struct comp_dev *dev, int cmd) +{
- return comp_set_state(dev, cmd);
+}
+/* used to pass standard and bespoke commands (with data) to component */ +static int fileread_cmd(struct comp_dev *dev, int cmd, void *data) +{
- struct sof_ipc_ctrl_data *cdata = data;
- int ret = 0;
- switch (cmd) {
- case COMP_CMD_SET_DATA:
ret = fr_cmd(dev, cdata);
break;
- default:
break;
- }
- return ret;
+}
+/* copy and process stream data from source to sink buffers
- returns the number of bytes copied
- */
+static int fileread_copy(struct comp_dev *dev) +{
- struct comp_buffer *sink;
- struct comp_buffer *source = NULL;
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- int ret = 0;
- /* fileread component sink buffer */
- sink = list_first_item(&dev->bsink_list, struct
comp_buffer,
source_list);
- /* Test that sink has enough free frames */
- if (sink->free >= cd->period_bytes && !cd-
frs.reached_eof) {
/* Read PCM samples from file */
ret = cd->fileread_func(dev, sink, source, dev-
frames);
if (ret > 0)
comp_update_buffer_produce(sink, ret *
dev->params.sample_container_bytes);
- }
- return ret;
+}
+static int fileread_prepare(struct comp_dev *dev) +{
- struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
- struct comp_buffer *sink_buffer;
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- int ret = 0;
- /* fileread component sink buffer */
- sink_buffer = list_first_item(&dev->bsink_list, struct
comp_buffer,
source_list);
- switch (config->frame_fmt) {
- case(SOF_IPC_FRAME_S16_LE):
+#if SHRINK_SINK == 1
/* set downstream buffer size */
ret = buffer_set_size(sink_buffer, dev->frames * 2
config->periods_sink * dev-
params.channels);
if (ret < 0) {
printf("error: fileread buffer size
set\n");
return ret;
}
+#endif
buffer_reset_pos(sink_buffer);
/* set fileread function */
cd->fileread_func = fileread_s16;
break;
- case(SOF_IPC_FRAME_S24_4LE):
+#if SHRINK_SINK == 1
/* set downstream buffer size */
ret = buffer_set_size(sink_buffer, dev->frames * 4
config->periods_sink * dev-
params.channels);
if (ret < 0) {
printf("error: fileread buffer size
set\n");
return ret;
}
+#endif
buffer_reset_pos(sink_buffer);
/* set fileread function */
cd->fileread_func = fileread_s24;
break;
- case(SOF_IPC_FRAME_S32_LE):
+#if SHRINK_SINK == 1
/* set downstream buffer size */
ret = buffer_set_size(sink_buffer, dev->frames * 4
config->periods_sink * dev-
params.channels);
if (ret < 0) {
printf("error: fileread buffer size
set\n");
return ret;
}
+#endif
buffer_reset_pos(sink_buffer);
break;
- default:
return -EINVAL;
- }
- dev->state = COMP_STATE_PREPARE;
- return ret;
+}
+static int fileread_reset(struct comp_dev *dev) +{
- dev->state = COMP_STATE_INIT;
- return 0;
+}
+struct comp_driver comp_fileread = {
- .type = SOF_COMP_FILEREAD,
- .ops = {
.new = fileread_new,
.free = fileread_free,
.params = fileread_params,
.cmd = fileread_cmd,
.trigger = fileread_trigger,
.copy = fileread_copy,
.prepare = fileread_prepare,
.reset = fileread_reset,
- },
+};
+void sys_comp_fileread_init(void) +{
- comp_register(&comp_fileread);
+} diff --git a/tune/common/filewrite.c b/tune/common/filewrite.c new file mode 100644 index 0000000..fc8d76e --- /dev/null +++ b/tune/common/filewrite.c @@ -0,0 +1,395 @@ +/*
- Copyright (c) 2016, 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@linux.intel.com
Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
- */
+#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <errno.h> +#include <sof/sof.h> +#include <sof/lock.h> +#include <sof/list.h> +#include <sof/stream.h> +#include <sof/work.h> +#include <sof/clock.h> +#include <sof/audio/component.h> +#include <sof/audio/format.h> +#include <sof/audio/pipeline.h> +#include "common_test.h"
+static inline void circular_inc_wrap(int32_t **ptr, int32_t *end,
size_t size)
+{
- if (*ptr >= end)
*ptr = (int32_t *) ((size_t)*ptr - size);
+}
+static inline void circular_inc_wrap_16(int16_t **ptr, int16_t *end,
size_t size)
+{
- if (*ptr >= end)
*ptr = (int16_t *) ((size_t)*ptr - size);
+}
+static int filewrite_s32_default(struct comp_dev *dev,
struct comp_buffer *sink,
struct comp_buffer *source,
uint32_t frames)
+{
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- int32_t *src = (int32_t *) source->r_ptr;
- int i, n, n_wrap_src, n_min, ret;
- int nch = dev->params.channels;
- int nwrite = 0;
- if (cd->fws.write_fail)
return -EINVAL;
- n = frames * nch;
- while (n > 0) {
n_wrap_src = (int32_t *) source->end_addr - src;
n_min = (n < n_wrap_src) ? n : n_wrap_src;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
ret = fprintf(cd->fws.fh, "%d\n",
*src);
nwrite++;
src++;
if (ret < 0) {
cd->fws.write_fail = 1;
goto quit;
}
}
}
circular_inc_wrap(&src, source->end_addr, source-
size);
- }
+quit:
- cd->fws.n += nwrite;
- return nwrite;
+}
+static int filewrite_s16(struct comp_dev *dev, struct comp_buffer *sink,
struct comp_buffer *source, uint32_t
frames) +{
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- int16_t *src = (int16_t *) source->r_ptr;
- int i, n, n_wrap_src, n_min, ret;
- int nch = dev->params.channels;
- int nwrite = 0;
- if (cd->fws.write_fail)
return -EINVAL;
- n = frames * nch;
- while (n > 0) {
n_wrap_src = (int16_t *) source->end_addr - src;
n_min = (n < n_wrap_src) ? n : n_wrap_src;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
ret = fprintf(cd->fws.fh, "%d\n",
*src);
nwrite++;
src++;
if (ret < 0) {
cd->fws.write_fail = 1;
goto quit;
}
}
}
circular_inc_wrap_16(&src, source->end_addr,
source->size);
- }
+quit:
- cd->fws.n += nwrite;
- return nwrite;
+}
+static int filewrite_s24(struct comp_dev *dev,
struct comp_buffer *sink,
struct comp_buffer *source,
uint32_t frames)
+{
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- int32_t *src = (int32_t *) source->r_ptr;
- int32_t se;
- int i, n, n_wrap_src, n_min, ret;
- int nch = dev->params.channels;
- int nwrite = 0;
- if (cd->fws.write_fail)
return -EINVAL;
- n = frames * nch;
- while (n > 0) {
n_wrap_src = (int32_t *) source->end_addr - src;
n_min = (n < n_wrap_src) ? n : n_wrap_src;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
se = *src << 8;
ret = fprintf(cd->fws.fh, "%d\n",
se >> 8);
nwrite++;
src++;
if (ret < 0) {
cd->fws.write_fail = 1;
goto quit;
}
}
}
circular_inc_wrap(&src, source->end_addr, source-
size);
- }
+quit:
- cd->fws.n += nwrite;
- return nwrite;
+}
+static struct comp_dev *filewrite_new(struct sof_ipc_comp *comp) +{
- struct comp_dev *dev;
- struct sof_ipc_comp_filewrite *filewrite;
- struct sof_ipc_comp_filewrite *ipc_filewrite
= (struct sof_ipc_comp_filewrite *) comp;
- struct filewrite_comp_data *cd;
- dev = malloc(COMP_SIZE(struct sof_ipc_comp_filewrite));
- if (dev == NULL)
return NULL;
- filewrite = (struct sof_ipc_comp_filewrite *) &dev->comp;
- memcpy(filewrite, ipc_filewrite, sizeof(struct
sof_ipc_comp_filewrite));
- cd = malloc(sizeof(*cd));
- if (cd == NULL) {
free(dev);
return NULL;
- }
- comp_set_drvdata(dev, cd);
- cd->filewrite_func = filewrite_s32_default;
- strncpy(cd->fws.fn, ipc_filewrite->fn,
FILEWRITE_FN_MAXLENGTH);
- cd->fws.fh = fopen(cd->fws.fn, "w");
- if (cd->fws.fh == NULL) {
fprintf(stderr, "Error: File %s open for write
failed.\n",
cd->fws.fn);
free(cd);
free(dev);
return NULL;
- }
- cd->fws.write_fail = 0;
- cd->fws.n = 0;
- return dev;
+}
+static void filewrite_free(struct comp_dev *dev) +{
- struct filewrite_data *td = comp_get_drvdata(dev);
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- fclose(cd->fws.fh);
- free(td);
- free(dev);
+}
+/* set component audio stream parameters */ +static int filewrite_params(struct comp_dev *dev) +{
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
- /* Need to compute this in non-dai endpoint */
- dev->frame_bytes =
dev->params.sample_container_bytes * dev-
params.channels;
- /* calculate period size based on config */
- cd->period_bytes = dev->frames * dev->frame_bytes;
- /* File to sink supports only S32_LE/S16_LE/S24_4LE PCM
formats */
- if ((config->frame_fmt != SOF_IPC_FRAME_S32_LE)
&& (config->frame_fmt != SOF_IPC_FRAME_S24_4LE)
&& (config->frame_fmt != SOF_IPC_FRAME_S16_LE))
return -EINVAL;
- return 0;
+}
+static int fw_cmd(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) +{
- return -EINVAL;
+}
+static int filewrite_trigger(struct comp_dev *dev, int cmd) +{
- return comp_set_state(dev, cmd);
+}
+/* used to pass standard and bespoke commands (with data) to component */ +static int filewrite_cmd(struct comp_dev *dev, int cmd, void *data) +{
- struct sof_ipc_ctrl_data *cdata = data;
- int ret = 0;
- switch (cmd) {
- case COMP_CMD_SET_DATA:
ret = fw_cmd(dev, cdata);
break;
- default:
break;
- }
- return ret;
+}
+/* copy and process stream data from source to sink buffers
- returns the number of bytes copied
- */
+static int filewrite_copy(struct comp_dev *dev) +{
- struct comp_buffer *sink = NULL;
- struct comp_buffer *source;
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- int ret = 0;
- /* Get filewrite component source buffer */
- source = list_first_item(&dev->bsource_list, struct
comp_buffer,
sink_list);
- /* Test that source has enough free frames */
- if (source->avail >= cd->period_bytes) {
/* Write PCM samples into file */
ret = cd->filewrite_func(dev, sink, source, dev-
frames);
if (ret < 0) {
printf("error: filewrite fail\n");
return ret;
}
comp_update_buffer_consume(source, ret *
dev-
params.sample_container_bytes);
- }
- return ret;
+}
+static int filewrite_prepare(struct comp_dev *dev) +{
- struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
- struct comp_buffer *source_buffer;
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- int ret = 0;
- /* fileread component sink buffer */
- source_buffer = list_first_item(&dev->bsource_list, struct
comp_buffer,
sink_list);
- switch (config->frame_fmt) {
- case(SOF_IPC_FRAME_S16_LE):
/* set downstream buffer size */
ret = buffer_set_size(source_buffer, dev->frames *
2 *
config->periods_source * dev-
params.channels);
if (ret < 0) {
printf("error: fileread buffer size
set\n");
return ret;
}
buffer_reset_pos(source_buffer);
/* set fileread function */
cd->filewrite_func = filewrite_s16;
break;
- case(SOF_IPC_FRAME_S24_4LE):
/* set downstream buffer size */
ret = buffer_set_size(source_buffer, dev->frames *
4 *
config->periods_source *
dev->params.channels);
if (ret < 0) {
printf("error: fileread buffer size
set\n");
return ret;
}
buffer_reset_pos(source_buffer);
/* set fileread function */
cd->filewrite_func = filewrite_s24;
break;
- case(SOF_IPC_FRAME_S32_LE):
/* set downstream buffer size */
ret = buffer_set_size(source_buffer, dev->frames *
4 *
config->periods_source *
dev->params.channels);
if (ret < 0) {
printf("error: fileread buffer size
set\n");
return ret;
}
buffer_reset_pos(source_buffer);
break;
- default:
return -EINVAL;
- }
- dev->state = COMP_STATE_PREPARE;
- return 0;
+}
+static int filewrite_reset(struct comp_dev *dev) +{
- dev->state = COMP_STATE_INIT;
- return 0;
+}
+struct comp_driver comp_filewrite = {
- .type = SOF_COMP_FILEWRITE,
- .ops = {
.new = filewrite_new,
.free = filewrite_free,
.params = filewrite_params,
.cmd = filewrite_cmd,
.trigger = filewrite_trigger,
.copy = filewrite_copy,
.prepare = filewrite_prepare,
.reset = filewrite_reset,
- },
+};
+void sys_comp_filewrite_init(void) +{
- comp_register(&comp_filewrite);
+}
Sound-open-firmware mailing list Sound-open-firmware@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/sound-open-firmware
On Wed, 2018-05-02 at 21:31 -0700, Ranjani Sridharan wrote:
This patch adds the fileread and filewrite components that testbench can liaise with to read in samples and write out processed samples.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com
tune/common/fileread.c | 385 ++++++++++++++++++++++++++++++++++++++++++++++ tune/common/filewrite.c | 395 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 780 insertions(+) create mode 100644 tune/common/fileread.c create mode 100644 tune/common/filewrite.c
diff --git a/tune/common/fileread.c b/tune/common/fileread.c
This should do bit R & W. i.e. we have file.c that can do duplex operations as this means we can reuse existing SSP based pipelines for test without modification.
new file mode 100644 index 0000000..0435fcf --- /dev/null +++ b/tune/common/fileread.c @@ -0,0 +1,385 @@ +/*
- Copyright (c) 2016, 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@linux.intel.com
Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
- */
+#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <errno.h> +#include <inttypes.h> +#include <sof/sof.h> +#include <sof/lock.h> +#include <sof/list.h> +#include <sof/stream.h> +#include <sof/work.h> +#include <sof/clock.h> +#include <sof/audio/component.h> +#include <sof/audio/format.h> +#include <sof/audio/pipeline.h> +#include <uapi/ipc.h> +#include "common_test.h"
+#define SHRINK_SINK 0
+static inline void circular_inc_wrap(int32_t **ptr, int32_t *end, size_t size)
buffer_check_wrap_s32() ?
+{
- if (*ptr >= end)
*ptr = (int32_t *) ((size_t)*ptr - size);
+}
+static inline void circular_inc_wrap_16(int16_t **ptr, int16_t *end,
size_t size)
+{
- if (*ptr >= end)
*ptr = (int16_t *) ((size_t)*ptr - size);
+}
+static int fileread_s32_default(struct comp_dev *dev, struct comp_buffer *sink,
struct comp_buffer *source, uint32_t frames)
+{
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- int32_t *dest = (int32_t *) sink->w_ptr;
- int i, n, n_wrap_dest, n_min, ret;
- int nch = dev->params.channels;
- int nread = 0;
can you add some comments below on the stages of this loop (and others).
- n = frames * nch;
- while (n > 0) {
n_wrap_dest = (int32_t *) sink->end_addr - dest;
n_min = (n < n_wrap_dest) ? n : n_wrap_dest;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
ret = fscanf(cd->frs.fh, "%d", dest);
nread++;
dest++;
if (ret == EOF)
goto read_eof;
}
}
circular_inc_wrap(&dest, sink->end_addr, sink->size);
- }
- cd->frs.n += nread;
- return nread;
+read_eof:
- cd->frs.n += nread;
- cd->frs.reached_eof = 1;
- return nread;
+}
+static int fileread_s16(struct comp_dev *dev, struct comp_buffer *sink,
- struct comp_buffer *source, uint32_t frames)
+{
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- int16_t *dest = (int16_t *) sink->w_ptr;
- int i, n, n_wrap_dest, n_min, ret;
- int nch = dev->params.channels;
- int nread = 0;
- n = frames * nch;
- while (n > 0) {
n_wrap_dest = (int16_t *) sink->end_addr - dest;
n_min = (n < n_wrap_dest) ? n : n_wrap_dest;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
ret = fscanf(cd->frs.fh, "%hd", dest);
nread++;
dest++;
if (ret == EOF)
goto read_eof;
}
}
circular_inc_wrap_16(&dest, sink->end_addr, sink->size);
- }
- cd->frs.n += nread;
- return nread;
+read_eof:
- cd->frs.n += nread;
- cd->frs.reached_eof = 1;
- return nread;
+}
+static int fileread_s24(struct comp_dev *dev, struct comp_buffer *sink,
- struct comp_buffer *source, uint32_t frames)
+{
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- int32_t *dest = (int32_t *) sink->w_ptr;
- int32_t sample;
- int i, n, n_wrap_dest, n_min, ret;
- int nch = dev->params.channels;
- int nread = 0;
- n = frames * nch;
- while (n > 0) {
n_wrap_dest = (int32_t *) sink->end_addr - dest;
n_min = (n < n_wrap_dest) ? n : n_wrap_dest;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
ret = fscanf(cd->frs.fh, "%d", &sample);
*dest = sample & 0x00ffffff; /* Mask bits */
nread++;
dest++;
if (ret == EOF)
goto read_eof;
}
}
circular_inc_wrap(&dest, sink->end_addr, sink->size);
- }
- cd->frs.n += nread;
- return nread;
+read_eof:
- cd->frs.n += nread;
- cd->frs.reached_eof = 1;
- return nread;
+}
+static struct comp_dev *fileread_new(struct sof_ipc_comp *comp) +{
- struct comp_dev *dev;
- struct sof_ipc_comp_fileread *fileread;
- struct sof_ipc_comp_fileread *ipc_fileread
= (struct sof_ipc_comp_fileread *) comp;
- struct fileread_comp_data *cd;
- dev = malloc(COMP_SIZE(struct sof_ipc_comp_fileread));
- if (dev == NULL)
return NULL;
- fileread = (struct sof_ipc_comp_fileread *) &dev->comp;
- memcpy(fileread, ipc_fileread, sizeof(struct sof_ipc_comp_fileread));
- cd = malloc(sizeof(*cd));
- if (cd == NULL) {
free(dev);
return NULL;
- }
- comp_set_drvdata(dev, cd);
- cd->fileread_func = fileread_s32_default;
- /* Get filename from IPC and open file */
- strcpy(cd->frs.fn, ipc_fileread->fn);
- cd->frs.fh = fopen(cd->frs.fn, "r");
- if (cd->frs.fh == NULL) {
fprintf(stderr, "Error: File %s open for read failed.\n",
cd->frs.fn);
free(cd);
free(dev);
return NULL;
- }
- cd->frs.reached_eof = 0;
- cd->frs.n = 0;
- return dev;
+}
+static void fileread_free(struct comp_dev *dev) +{
- struct fileread_data *td = comp_get_drvdata(dev);
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- fclose(cd->frs.fh);
- free(td);
- free(dev);
+}
+/* set component audio stream parameters */ +static int fileread_params(struct comp_dev *dev) +{
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
- /* Need to compute this in non-host endpoint */
- dev->frame_bytes =
dev->params.sample_container_bytes * dev->params.channels;
- /* calculate period size based on config */
- cd->period_bytes = dev->frames * dev->frame_bytes;
- /* File to sink supports only S32_LE/S16_LE/S24_4LE PCM formats */
- if ((config->frame_fmt != SOF_IPC_FRAME_S32_LE)
&& (config->frame_fmt != SOF_IPC_FRAME_S24_4LE)
&& (config->frame_fmt != SOF_IPC_FRAME_S16_LE))
return -EINVAL;
- return 0;
+}
+static int fr_cmd(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) +{
- return -EINVAL;
+}
+static int fileread_trigger(struct comp_dev *dev, int cmd) +{
- return comp_set_state(dev, cmd);
+}
+/* used to pass standard and bespoke commands (with data) to component */ +static int fileread_cmd(struct comp_dev *dev, int cmd, void *data) +{
- struct sof_ipc_ctrl_data *cdata = data;
- int ret = 0;
- switch (cmd) {
- case COMP_CMD_SET_DATA:
ret = fr_cmd(dev, cdata);
break;
- default:
break;
- }
- return ret;
+}
+/* copy and process stream data from source to sink buffers
- returns the number of bytes copied
- */
+static int fileread_copy(struct comp_dev *dev) +{
- struct comp_buffer *sink;
- struct comp_buffer *source = NULL;
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- int ret = 0;
- /* fileread component sink buffer */
- sink = list_first_item(&dev->bsink_list, struct comp_buffer,
source_list);
- /* Test that sink has enough free frames */
- if (sink->free >= cd->period_bytes && !cd->frs.reached_eof) {
/* Read PCM samples from file */
ret = cd->fileread_func(dev, sink, source, dev->frames);
if (ret > 0)
comp_update_buffer_produce(sink, ret *
dev->params.sample_container_bytes);
- }
- return ret;
+}
+static int fileread_prepare(struct comp_dev *dev) +{
- struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
- struct comp_buffer *sink_buffer;
- struct fileread_comp_data *cd = comp_get_drvdata(dev);
- int ret = 0;
- /* fileread component sink buffer */
- sink_buffer = list_first_item(&dev->bsink_list, struct comp_buffer,
source_list);
- switch (config->frame_fmt) {
- case(SOF_IPC_FRAME_S16_LE):
+#if SHRINK_SINK == 1
/* set downstream buffer size */
ret = buffer_set_size(sink_buffer, dev->frames * 2 *
config->periods_sink * dev->params.channels);
if (ret < 0) {
printf("error: fileread buffer size set\n");
return ret;
}
+#endif
buffer_reset_pos(sink_buffer);
/* set fileread function */
cd->fileread_func = fileread_s16;
break;
- case(SOF_IPC_FRAME_S24_4LE):
+#if SHRINK_SINK == 1
/* set downstream buffer size */
ret = buffer_set_size(sink_buffer, dev->frames * 4 *
config->periods_sink * dev->params.channels);
if (ret < 0) {
printf("error: fileread buffer size set\n");
return ret;
}
+#endif
buffer_reset_pos(sink_buffer);
/* set fileread function */
cd->fileread_func = fileread_s24;
break;
- case(SOF_IPC_FRAME_S32_LE):
+#if SHRINK_SINK == 1
/* set downstream buffer size */
ret = buffer_set_size(sink_buffer, dev->frames * 4 *
config->periods_sink * dev->params.channels);
if (ret < 0) {
printf("error: fileread buffer size set\n");
return ret;
}
+#endif
buffer_reset_pos(sink_buffer);
break;
- default:
return -EINVAL;
- }
- dev->state = COMP_STATE_PREPARE;
- return ret;
+}
+static int fileread_reset(struct comp_dev *dev) +{
- dev->state = COMP_STATE_INIT;
- return 0;
+}
+struct comp_driver comp_fileread = {
- .type = SOF_COMP_FILEREAD,
- .ops = {
.new = fileread_new,
.free = fileread_free,
.params = fileread_params,
.cmd = fileread_cmd,
.trigger = fileread_trigger,
.copy = fileread_copy,
.prepare = fileread_prepare,
.reset = fileread_reset,
- },
+};
+void sys_comp_fileread_init(void) +{
- comp_register(&comp_fileread);
+} diff --git a/tune/common/filewrite.c b/tune/common/filewrite.c new file mode 100644 index 0000000..fc8d76e --- /dev/null +++ b/tune/common/filewrite.c @@ -0,0 +1,395 @@ +/*
- Copyright (c) 2016, 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@linux.intel.com
Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
- */
+#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <errno.h> +#include <sof/sof.h> +#include <sof/lock.h> +#include <sof/list.h> +#include <sof/stream.h> +#include <sof/work.h> +#include <sof/clock.h> +#include <sof/audio/component.h> +#include <sof/audio/format.h> +#include <sof/audio/pipeline.h> +#include "common_test.h"
+static inline void circular_inc_wrap(int32_t **ptr, int32_t *end,
size_t size)
+{
- if (*ptr >= end)
*ptr = (int32_t *) ((size_t)*ptr - size);
+}
+static inline void circular_inc_wrap_16(int16_t **ptr, int16_t *end,
size_t size)
+{
- if (*ptr >= end)
*ptr = (int16_t *) ((size_t)*ptr - size);
+}
+static int filewrite_s32_default(struct comp_dev *dev,
struct comp_buffer *sink,
struct comp_buffer *source,
uint32_t frames)
+{
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- int32_t *src = (int32_t *) source->r_ptr;
- int i, n, n_wrap_src, n_min, ret;
- int nch = dev->params.channels;
- int nwrite = 0;
- if (cd->fws.write_fail)
return -EINVAL;
- n = frames * nch;
- while (n > 0) {
n_wrap_src = (int32_t *) source->end_addr - src;
n_min = (n < n_wrap_src) ? n : n_wrap_src;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
ret = fprintf(cd->fws.fh, "%d\n", *src);
nwrite++;
src++;
if (ret < 0) {
cd->fws.write_fail = 1;
goto quit;
}
}
}
circular_inc_wrap(&src, source->end_addr, source->size);
- }
+quit:
- cd->fws.n += nwrite;
- return nwrite;
+}
+static int filewrite_s16(struct comp_dev *dev, struct comp_buffer *sink,
struct comp_buffer *source, uint32_t frames)
+{
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- int16_t *src = (int16_t *) source->r_ptr;
- int i, n, n_wrap_src, n_min, ret;
- int nch = dev->params.channels;
- int nwrite = 0;
- if (cd->fws.write_fail)
return -EINVAL;
- n = frames * nch;
- while (n > 0) {
n_wrap_src = (int16_t *) source->end_addr - src;
n_min = (n < n_wrap_src) ? n : n_wrap_src;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
ret = fprintf(cd->fws.fh, "%d\n", *src);
nwrite++;
src++;
if (ret < 0) {
cd->fws.write_fail = 1;
goto quit;
}
}
}
circular_inc_wrap_16(&src, source->end_addr, source->size);
- }
+quit:
- cd->fws.n += nwrite;
- return nwrite;
+}
+static int filewrite_s24(struct comp_dev *dev,
struct comp_buffer *sink,
struct comp_buffer *source,
uint32_t frames)
+{
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- int32_t *src = (int32_t *) source->r_ptr;
- int32_t se;
- int i, n, n_wrap_src, n_min, ret;
- int nch = dev->params.channels;
- int nwrite = 0;
- if (cd->fws.write_fail)
return -EINVAL;
- n = frames * nch;
- while (n > 0) {
n_wrap_src = (int32_t *) source->end_addr - src;
n_min = (n < n_wrap_src) ? n : n_wrap_src;
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
se = *src << 8;
ret = fprintf(cd->fws.fh, "%d\n", se >> 8);
nwrite++;
src++;
if (ret < 0) {
cd->fws.write_fail = 1;
goto quit;
}
}
}
circular_inc_wrap(&src, source->end_addr, source->size);
- }
+quit:
- cd->fws.n += nwrite;
- return nwrite;
+}
+static struct comp_dev *filewrite_new(struct sof_ipc_comp *comp) +{
- struct comp_dev *dev;
- struct sof_ipc_comp_filewrite *filewrite;
- struct sof_ipc_comp_filewrite *ipc_filewrite
= (struct sof_ipc_comp_filewrite *) comp;
- struct filewrite_comp_data *cd;
- dev = malloc(COMP_SIZE(struct sof_ipc_comp_filewrite));
- if (dev == NULL)
return NULL;
- filewrite = (struct sof_ipc_comp_filewrite *) &dev->comp;
- memcpy(filewrite, ipc_filewrite, sizeof(struct
sof_ipc_comp_filewrite));
- cd = malloc(sizeof(*cd));
- if (cd == NULL) {
free(dev);
return NULL;
- }
- comp_set_drvdata(dev, cd);
- cd->filewrite_func = filewrite_s32_default;
- strncpy(cd->fws.fn, ipc_filewrite->fn, FILEWRITE_FN_MAXLENGTH);
- cd->fws.fh = fopen(cd->fws.fn, "w");
- if (cd->fws.fh == NULL) {
fprintf(stderr, "Error: File %s open for write failed.\n",
cd->fws.fn);
free(cd);
free(dev);
return NULL;
- }
- cd->fws.write_fail = 0;
- cd->fws.n = 0;
- return dev;
+}
+static void filewrite_free(struct comp_dev *dev) +{
- struct filewrite_data *td = comp_get_drvdata(dev);
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- fclose(cd->fws.fh);
- free(td);
- free(dev);
+}
+/* set component audio stream parameters */ +static int filewrite_params(struct comp_dev *dev) +{
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
- /* Need to compute this in non-dai endpoint */
- dev->frame_bytes =
dev->params.sample_container_bytes * dev->params.channels;
- /* calculate period size based on config */
- cd->period_bytes = dev->frames * dev->frame_bytes;
- /* File to sink supports only S32_LE/S16_LE/S24_4LE PCM formats */
- if ((config->frame_fmt != SOF_IPC_FRAME_S32_LE)
&& (config->frame_fmt != SOF_IPC_FRAME_S24_4LE)
&& (config->frame_fmt != SOF_IPC_FRAME_S16_LE))
return -EINVAL;
- return 0;
+}
+static int fw_cmd(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) +{
- return -EINVAL;
+}
+static int filewrite_trigger(struct comp_dev *dev, int cmd) +{
- return comp_set_state(dev, cmd);
+}
+/* used to pass standard and bespoke commands (with data) to component */ +static int filewrite_cmd(struct comp_dev *dev, int cmd, void *data) +{
- struct sof_ipc_ctrl_data *cdata = data;
- int ret = 0;
- switch (cmd) {
- case COMP_CMD_SET_DATA:
ret = fw_cmd(dev, cdata);
break;
- default:
break;
- }
- return ret;
+}
+/* copy and process stream data from source to sink buffers
- returns the number of bytes copied
- */
+static int filewrite_copy(struct comp_dev *dev) +{
- struct comp_buffer *sink = NULL;
- struct comp_buffer *source;
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- int ret = 0;
- /* Get filewrite component source buffer */
- source = list_first_item(&dev->bsource_list, struct comp_buffer,
sink_list);
- /* Test that source has enough free frames */
- if (source->avail >= cd->period_bytes) {
/* Write PCM samples into file */
ret = cd->filewrite_func(dev, sink, source, dev->frames);
if (ret < 0) {
printf("error: filewrite fail\n");
return ret;
}
comp_update_buffer_consume(source, ret *
dev-
params.sample_container_bytes);
- }
- return ret;
+}
+static int filewrite_prepare(struct comp_dev *dev) +{
- struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
- struct comp_buffer *source_buffer;
- struct filewrite_comp_data *cd = comp_get_drvdata(dev);
- int ret = 0;
- /* fileread component sink buffer */
- source_buffer = list_first_item(&dev->bsource_list, struct
comp_buffer,
sink_list);
- switch (config->frame_fmt) {
- case(SOF_IPC_FRAME_S16_LE):
/* set downstream buffer size */
ret = buffer_set_size(source_buffer, dev->frames * 2 *
config->periods_source * dev->params.channels);
if (ret < 0) {
printf("error: fileread buffer size set\n");
return ret;
}
buffer_reset_pos(source_buffer);
/* set fileread function */
cd->filewrite_func = filewrite_s16;
break;
- case(SOF_IPC_FRAME_S24_4LE):
/* set downstream buffer size */
ret = buffer_set_size(source_buffer, dev->frames * 4 *
config->periods_source *
dev->params.channels);
if (ret < 0) {
printf("error: fileread buffer size set\n");
return ret;
}
buffer_reset_pos(source_buffer);
/* set fileread function */
cd->filewrite_func = filewrite_s24;
break;
- case(SOF_IPC_FRAME_S32_LE):
/* set downstream buffer size */
ret = buffer_set_size(source_buffer, dev->frames * 4 *
config->periods_source *
dev->params.channels);
if (ret < 0) {
printf("error: fileread buffer size set\n");
return ret;
}
buffer_reset_pos(source_buffer);
break;
- default:
return -EINVAL;
- }
- dev->state = COMP_STATE_PREPARE;
- return 0;
+}
+static int filewrite_reset(struct comp_dev *dev) +{
- dev->state = COMP_STATE_INIT;
- return 0;
+}
+struct comp_driver comp_filewrite = {
- .type = SOF_COMP_FILEWRITE,
- .ops = {
.new = filewrite_new,
.free = filewrite_free,
.params = filewrite_params,
.cmd = filewrite_cmd,
.trigger = filewrite_trigger,
.copy = filewrite_copy,
.prepare = filewrite_prepare,
.reset = filewrite_reset,
- },
+};
+void sys_comp_filewrite_init(void) +{
- comp_register(&comp_filewrite);
+}
This patch adds a volume testbench that enable reading samples from an input file, processing them through a volume pipeline and writing the processed samples to the output file.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com --- configure.ac | 1 + tune/Makefile.am | 2 +- tune/volume/Makefile.am | 17 +++++ tune/volume/volume_test.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 tune/volume/Makefile.am create mode 100644 tune/volume/volume_test.c
diff --git a/configure.ac b/configure.ac index d1c288d..6cc63bc 100644 --- a/configure.ac +++ b/configure.ac @@ -24,6 +24,7 @@ AC_OUTPUT([ topology/test/Makefile tune/Makefile tune/common/Makefile + tune/volume/Makefile ])
echo " diff --git a/tune/Makefile.am b/tune/Makefile.am index e2e95a6..e7b9c0c 100644 --- a/tune/Makefile.am +++ b/tune/Makefile.am @@ -1 +1 @@ -SUBDIRS = common +SUBDIRS = common volume diff --git a/tune/volume/Makefile.am b/tune/volume/Makefile.am new file mode 100644 index 0000000..2037802 --- /dev/null +++ b/tune/volume/Makefile.am @@ -0,0 +1,17 @@ +AUTOMAKE_OPTIONS = subdir-objects + +INCDIR1 = ../common +INCDIR2 = $(prefix)/include/sof +INCLUDE = -I$(INCDIR1) -I$(INCDIR2) +DEFINE = -DDEBUG_PRINT + +AM_CPPFLAGS = $(INCLUDE) $(DEFINE) +AM_CFLAGS = -g -Wall +LDADD = \ + ../common/libtb_common.a \ + -lm -lsof -lsof_ipc -lsof_volume + +bin_PROGRAMS = volume_test + +volume_test_SOURCES = \ + volume_test.c diff --git a/tune/volume/volume_test.c b/tune/volume/volume_test.c new file mode 100644 index 0000000..72fcb02 --- /dev/null +++ b/tune/volume/volume_test.c @@ -0,0 +1,160 @@ +/* + * 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@linux.intel.com + * Ranjani Sridharan ranjani.sridharan@linux.intel.com + */ + +#include <sof/ipc.h> +#include <getopt.h> +#include "common_test.h" +#include "common_tplg.h" + +#define TESTBENCH_NCH 2 /* Stereo */ + +/* main firmware context */ +static struct sof sof; +static int fr_id; /* comp id for fileread */ +static int fw_id; /* comp id for filewrite */ +static int sched_id; /* comp id for scheduling comp */ + +static void print_usage(char *executable) +{ + printf("Usage: %s -i <input_file> -o <output_file> -t <tplg_file> -b <input_format>\n", + executable); + printf("input_format should be s16le, s32le, s42le or float\n"); +} + +int main(int argc, char **argv) +{ + struct ipc_comp_dev *pcm_dev; + struct pipeline *p; + struct sof_ipc_pipe_new *ipc_pipe; + struct comp_dev *cd; + struct fileread_comp_data *frcd; + struct filewrite_comp_data *fwcd; + char *tplg_file = NULL, *input_file = NULL; + char *output_file = NULL, *bits_in = NULL; + clock_t tic, toc; + double c_realtime, t_exec; + int fs, n_in, n_out, ret; + int option = 0; + + /* command line arguments*/ + while ((option = getopt(argc, argv, "i:o:t:b:")) != -1) { + switch (option) { + case 'i': + input_file = malloc(strlen(optarg)); + strcpy(input_file, optarg); + break; + case 'o': + output_file = malloc(strlen(optarg)); + strcpy(output_file, optarg); + break; + case 't': + tplg_file = malloc(strlen(optarg)); + strcpy(tplg_file, optarg); + break; + case 'b': + bits_in = malloc(strlen(optarg)); + strcpy(bits_in, optarg); + break; + default: + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + } + + if (!bits_in || !tplg_file || !input_file || !output_file) { + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + + /* initialize ipc, pipeline and scheduler */ + if (tb_pipeline_setup(&sof) < 0) { + printf("error: pipeline init\n"); + exit(EXIT_FAILURE); + } + + /* parse topology file and create pipeline */ + if (parse_topology(tplg_file, &sof, &fr_id, &fw_id, &sched_id, bits_in, + input_file, output_file) < 0) { + printf("error: parsing topology\n"); + exit(EXIT_FAILURE); + } + + /* Get pointers to fileread and filewrite */ + pcm_dev = ipc_get_comp(sof.ipc, fw_id); + fwcd = comp_get_drvdata(pcm_dev->cd); + pcm_dev = ipc_get_comp(sof.ipc, fr_id); + frcd = comp_get_drvdata(pcm_dev->cd); + + /* Run pipeline until EOF from fileread */ + pcm_dev = ipc_get_comp(sof.ipc, sched_id); + p = pcm_dev->cd->pipeline; + ipc_pipe = &p->ipc_pipe; + + fs = ipc_pipe->deadline * ipc_pipe->frames_per_sched; + + /* set pipeline params and trigger start */ + if (tb_pipeline_start(sof.ipc, TESTBENCH_NCH, bits_in, ipc_pipe) < 0) { + printf("error: pipeline params\n"); + exit(EXIT_FAILURE); + } + cd = pcm_dev->cd; + tb_disable_trace(); /* Reduce print output */ + tic = clock(); + + while (frcd->frs.reached_eof == 0) + pipeline_schedule_copy(p, 0); + + /* reset and free pipeline */ + toc = clock(); + tb_enable_trace(); + ret = pipeline_reset(p, cd); + if (ret < 0) { + printf("error: pipeline reset\n"); + exit(EXIT_FAILURE); + } + pipeline_free(p); + + n_in = frcd->frs.n; + n_out = fwcd->fws.n; + t_exec = (double) (toc - tic) / CLOCKS_PER_SEC; + c_realtime = (double) n_out / TESTBENCH_NCH / fs / t_exec; + /* print test summary */ + printf("==============================================\n"); + printf(" Test Summary\n"); + printf("==============================================\n"); + printf("Output written to file "%s"\n", output_file); + printf("Input sample count: %d\n", n_in); + printf("Output sample count: %d\n", n_out); + printf("Total execution time: %.2f us, %.2f x realtime\n", + 1e3 * t_exec, c_realtime); + + return EXIT_SUCCESS; +}
On 03.05.2018 07:31, Ranjani Sridharan wrote:
This patch adds a volume testbench that enable reading samples from an input file, processing them through a volume pipeline and writing the processed samples to the output file.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com
configure.ac | 1 + tune/Makefile.am | 2 +- tune/volume/Makefile.am | 17 +++++ tune/volume/volume_test.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 tune/volume/Makefile.am create mode 100644 tune/volume/volume_test.c
diff --git a/configure.ac b/configure.ac index d1c288d..6cc63bc 100644 --- a/configure.ac +++ b/configure.ac @@ -24,6 +24,7 @@ AC_OUTPUT([ topology/test/Makefile tune/Makefile tune/common/Makefile
tune/volume/Makefile ])
echo "
diff --git a/tune/Makefile.am b/tune/Makefile.am index e2e95a6..e7b9c0c 100644 --- a/tune/Makefile.am +++ b/tune/Makefile.am @@ -1 +1 @@ -SUBDIRS = common +SUBDIRS = common volume diff --git a/tune/volume/Makefile.am b/tune/volume/Makefile.am new file mode 100644 index 0000000..2037802 --- /dev/null +++ b/tune/volume/Makefile.am @@ -0,0 +1,17 @@ +AUTOMAKE_OPTIONS = subdir-objects
+INCDIR1 = ../common +INCDIR2 = $(prefix)/include/sof +INCLUDE = -I$(INCDIR1) -I$(INCDIR2) +DEFINE = -DDEBUG_PRINT
+AM_CPPFLAGS = $(INCLUDE) $(DEFINE) +AM_CFLAGS = -g -Wall +LDADD = \
- ../common/libtb_common.a \
- -lm -lsof -lsof_ipc -lsof_volume
+bin_PROGRAMS = volume_test
+volume_test_SOURCES = \
- volume_test.c
diff --git a/tune/volume/volume_test.c b/tune/volume/volume_test.c new file mode 100644 index 0000000..72fcb02 --- /dev/null +++ b/tune/volume/volume_test.c @@ -0,0 +1,160 @@ +/*
- 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@linux.intel.com
Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
- */
+#include <sof/ipc.h> +#include <getopt.h> +#include "common_test.h" +#include "common_tplg.h"
+#define TESTBENCH_NCH 2 /* Stereo */
Number of channels would be useful as command line argument (default to stereo if omitted) or get it from topology parsing if it's defined there.
+/* main firmware context */ +static struct sof sof; +static int fr_id; /* comp id for fileread */ +static int fw_id; /* comp id for filewrite */ +static int sched_id; /* comp id for scheduling comp */
+static void print_usage(char *executable) +{
- printf("Usage: %s -i <input_file> -o <output_file> -t <tplg_file> -b <input_format>\n",
executable);
- printf("input_format should be s16le, s32le, s42le or float\n");
Typo, s24le?
+}
+int main(int argc, char **argv) +{
- struct ipc_comp_dev *pcm_dev;
- struct pipeline *p;
- struct sof_ipc_pipe_new *ipc_pipe;
- struct comp_dev *cd;
- struct fileread_comp_data *frcd;
- struct filewrite_comp_data *fwcd;
- char *tplg_file = NULL, *input_file = NULL;
- char *output_file = NULL, *bits_in = NULL;
- clock_t tic, toc;
- double c_realtime, t_exec;
- int fs, n_in, n_out, ret;
- int option = 0;
- /* command line arguments*/
- while ((option = getopt(argc, argv, "i:o:t:b:")) != -1) {
switch (option) {
case 'i':
input_file = malloc(strlen(optarg));
strcpy(input_file, optarg);
break;
case 'o':
output_file = malloc(strlen(optarg));
strcpy(output_file, optarg);
break;
case 't':
tplg_file = malloc(strlen(optarg));
strcpy(tplg_file, optarg);
break;
case 'b':
bits_in = malloc(strlen(optarg));
strcpy(bits_in, optarg);
break;
default:
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
- }
- if (!bits_in || !tplg_file || !input_file || !output_file) {
print_usage(argv[0]);
exit(EXIT_FAILURE);
- }
- /* initialize ipc, pipeline and scheduler */
- if (tb_pipeline_setup(&sof) < 0) {
printf("error: pipeline init\n");
exit(EXIT_FAILURE);
- }
- /* parse topology file and create pipeline */
- if (parse_topology(tplg_file, &sof, &fr_id, &fw_id, &sched_id, bits_in,
input_file, output_file) < 0) {
printf("error: parsing topology\n");
exit(EXIT_FAILURE);
- }
- /* Get pointers to fileread and filewrite */
- pcm_dev = ipc_get_comp(sof.ipc, fw_id);
- fwcd = comp_get_drvdata(pcm_dev->cd);
- pcm_dev = ipc_get_comp(sof.ipc, fr_id);
- frcd = comp_get_drvdata(pcm_dev->cd);
- /* Run pipeline until EOF from fileread */
- pcm_dev = ipc_get_comp(sof.ipc, sched_id);
- p = pcm_dev->cd->pipeline;
- ipc_pipe = &p->ipc_pipe;
- fs = ipc_pipe->deadline * ipc_pipe->frames_per_sched;
- /* set pipeline params and trigger start */
- if (tb_pipeline_start(sof.ipc, TESTBENCH_NCH, bits_in, ipc_pipe) < 0) {
printf("error: pipeline params\n");
exit(EXIT_FAILURE);
- }
- cd = pcm_dev->cd;
- tb_disable_trace(); /* Reduce print output */
- tic = clock();
- while (frcd->frs.reached_eof == 0)
pipeline_schedule_copy(p, 0);
- /* reset and free pipeline */
- toc = clock();
- tb_enable_trace();
- ret = pipeline_reset(p, cd);
- if (ret < 0) {
printf("error: pipeline reset\n");
exit(EXIT_FAILURE);
- }
- pipeline_free(p);
- n_in = frcd->frs.n;
- n_out = fwcd->fws.n;
- t_exec = (double) (toc - tic) / CLOCKS_PER_SEC;
- c_realtime = (double) n_out / TESTBENCH_NCH / fs / t_exec;
- /* print test summary */
- printf("==============================================\n");
- printf(" Test Summary\n");
- printf("==============================================\n");
- printf("Output written to file "%s"\n", output_file);
- printf("Input sample count: %d\n", n_in);
- printf("Output sample count: %d\n", n_out);
- printf("Total execution time: %.2f us, %.2f x realtime\n",
1e3 * t_exec, c_realtime);
- return EXIT_SUCCESS;
+}
On Wed, 2018-05-02 at 21:31 -0700, Ranjani Sridharan wrote:
This patch adds a volume testbench that enable reading samples from an input file, processing them through a volume pipeline and writing the processed samples to the output file.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com
configure.ac | 1 + tune/Makefile.am | 2 +- tune/volume/Makefile.am | 17 +++++ tune/volume/volume_test.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 tune/volume/Makefile.am create mode 100644 tune/volume/volume_test.c
diff --git a/configure.ac b/configure.ac index d1c288d..6cc63bc 100644 --- a/configure.ac +++ b/configure.ac @@ -24,6 +24,7 @@ AC_OUTPUT([ topology/test/Makefile tune/Makefile tune/common/Makefile
- tune/volume/Makefile
])
echo " diff --git a/tune/Makefile.am b/tune/Makefile.am index e2e95a6..e7b9c0c 100644 --- a/tune/Makefile.am +++ b/tune/Makefile.am @@ -1 +1 @@ -SUBDIRS = common +SUBDIRS = common volume diff --git a/tune/volume/Makefile.am b/tune/volume/Makefile.am new file mode 100644 index 0000000..2037802 --- /dev/null +++ b/tune/volume/Makefile.am @@ -0,0 +1,17 @@ +AUTOMAKE_OPTIONS = subdir-objects
+INCDIR1 = ../common +INCDIR2 = $(prefix)/include/sof +INCLUDE = -I$(INCDIR1) -I$(INCDIR2) +DEFINE = -DDEBUG_PRINT
+AM_CPPFLAGS = $(INCLUDE) $(DEFINE) +AM_CFLAGS = -g -Wall +LDADD = \
- ../common/libtb_common.a \
- -lm -lsof -lsof_ipc -lsof_volume
+bin_PROGRAMS = volume_test
+volume_test_SOURCES = \
- volume_test.c
diff --git a/tune/volume/volume_test.c b/tune/volume/volume_test.c new file mode 100644 index 0000000..72fcb02 --- /dev/null +++ b/tune/volume/volume_test.c @@ -0,0 +1,160 @@ +/*
- 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@linux.intel.com
Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
- */
+#include <sof/ipc.h> +#include <getopt.h> +#include "common_test.h" +#include "common_tplg.h"
+#define TESTBENCH_NCH 2 /* Stereo */
+/* main firmware context */ +static struct sof sof; +static int fr_id; /* comp id for fileread */ +static int fw_id; /* comp id for filewrite */ +static int sched_id; /* comp id for scheduling comp */
+static void print_usage(char *executable) +{
- printf("Usage: %s -i <input_file> -o <output_file> -t <tplg_file> -b
<input_format>\n",
executable);
- printf("input_format should be s16le, s32le, s42le or float\n");
s42 ? Can we make these the same as ALSA, i.e. S32_LE
Btw, I'm assuming the audio components are all built-in to the binary ? We do need this to be modular in the future so we can open binary modules (as .so shared objects) using dlopen() and alias them.
e.g. sof-test -i file -o file -t file -a vol=vol_avx2,src=super_src
This would mean that the test bench would load all components as .so and overide the default volume and SRC with vol_avx2 and super_src respectively.
+}
+int main(int argc, char **argv) +{
- struct ipc_comp_dev *pcm_dev;
- struct pipeline *p;
- struct sof_ipc_pipe_new *ipc_pipe;
- struct comp_dev *cd;
- struct fileread_comp_data *frcd;
- struct filewrite_comp_data *fwcd;
- char *tplg_file = NULL, *input_file = NULL;
- char *output_file = NULL, *bits_in = NULL;
- clock_t tic, toc;
- double c_realtime, t_exec;
- int fs, n_in, n_out, ret;
- int option = 0;
- /* command line arguments*/
- while ((option = getopt(argc, argv, "i:o:t:b:")) != -1) {
switch (option) {
case 'i':
input_file = malloc(strlen(optarg));
strcpy(input_file, optarg);
strdup() ?
break;
case 'o':
output_file = malloc(strlen(optarg));
strcpy(output_file, optarg);
break;
case 't':
tplg_file = malloc(strlen(optarg));
strcpy(tplg_file, optarg);
break;
case 'b':
bits_in = malloc(strlen(optarg));
strcpy(bits_in, optarg);
break;
default:
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
- }
- if (!bits_in || !tplg_file || !input_file || !output_file) {
print_usage(argv[0]);
exit(EXIT_FAILURE);
- }
- /* initialize ipc, pipeline and scheduler */
- if (tb_pipeline_setup(&sof) < 0) {
printf("error: pipeline init\n");
exit(EXIT_FAILURE);
- }
- /* parse topology file and create pipeline */
- if (parse_topology(tplg_file, &sof, &fr_id, &fw_id, &sched_id,
bits_in,
input_file, output_file) < 0) {
printf("error: parsing topology\n");
exit(EXIT_FAILURE);
- }
- /* Get pointers to fileread and filewrite */
- pcm_dev = ipc_get_comp(sof.ipc, fw_id);
- fwcd = comp_get_drvdata(pcm_dev->cd);
- pcm_dev = ipc_get_comp(sof.ipc, fr_id);
- frcd = comp_get_drvdata(pcm_dev->cd);
I'd expect the topology DAIs to be converted to file read/write components. i.e. a pipeline like :-
HOST -> B0 -> VOL0 -> B1 -> SSP
Would convert it's source/sink endpoints to file component.
- /* Run pipeline until EOF from fileread */
- pcm_dev = ipc_get_comp(sof.ipc, sched_id);
- p = pcm_dev->cd->pipeline;
- ipc_pipe = &p->ipc_pipe;
- fs = ipc_pipe->deadline * ipc_pipe->frames_per_sched;
- /* set pipeline params and trigger start */
- if (tb_pipeline_start(sof.ipc, TESTBENCH_NCH, bits_in, ipc_pipe) < 0)
{
printf("error: pipeline params\n");
exit(EXIT_FAILURE);
- }
- cd = pcm_dev->cd;
- tb_disable_trace(); /* Reduce print output */
- tic = clock();
- while (frcd->frs.reached_eof == 0)
pipeline_schedule_copy(p, 0);
- /* reset and free pipeline */
- toc = clock();
- tb_enable_trace();
- ret = pipeline_reset(p, cd);
- if (ret < 0) {
printf("error: pipeline reset\n");
exit(EXIT_FAILURE);
- }
- pipeline_free(p);
- n_in = frcd->frs.n;
- n_out = fwcd->fws.n;
- t_exec = (double) (toc - tic) / CLOCKS_PER_SEC;
- c_realtime = (double) n_out / TESTBENCH_NCH / fs / t_exec;
- /* print test summary */
- printf("==============================================\n");
- printf(" Test Summary\n");
- printf("==============================================\n");
- printf("Output written to file "%s"\n", output_file);
- printf("Input sample count: %d\n", n_in);
- printf("Output sample count: %d\n", n_out);
- printf("Total execution time: %.2f us, %.2f x realtime\n",
1e3 * t_exec, c_realtime);
- return EXIT_SUCCESS;
+}
On Mon, 2018-05-07 at 16:07 +0100, Liam Girdwood wrote:
On Wed, 2018-05-02 at 21:31 -0700, Ranjani Sridharan wrote:
This patch adds a volume testbench that enable reading samples from an input file, processing them through a volume pipeline and writing the processed samples to the output file.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com
configure.ac | 1 + tune/Makefile.am | 2 +- tune/volume/Makefile.am | 17 +++++ tune/volume/volume_test.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 tune/volume/Makefile.am create mode 100644 tune/volume/volume_test.c
diff --git a/configure.ac b/configure.ac index d1c288d..6cc63bc 100644 --- a/configure.ac +++ b/configure.ac @@ -24,6 +24,7 @@ AC_OUTPUT([ topology/test/Makefile tune/Makefile tune/common/Makefile
- tune/volume/Makefile
])
echo " diff --git a/tune/Makefile.am b/tune/Makefile.am index e2e95a6..e7b9c0c 100644 --- a/tune/Makefile.am +++ b/tune/Makefile.am @@ -1 +1 @@ -SUBDIRS = common +SUBDIRS = common volume diff --git a/tune/volume/Makefile.am b/tune/volume/Makefile.am new file mode 100644 index 0000000..2037802 --- /dev/null +++ b/tune/volume/Makefile.am @@ -0,0 +1,17 @@ +AUTOMAKE_OPTIONS = subdir-objects
+INCDIR1 = ../common +INCDIR2 = $(prefix)/include/sof +INCLUDE = -I$(INCDIR1) -I$(INCDIR2) +DEFINE = -DDEBUG_PRINT
+AM_CPPFLAGS = $(INCLUDE) $(DEFINE) +AM_CFLAGS = -g -Wall +LDADD = \
- ../common/libtb_common.a \
- -lm -lsof -lsof_ipc -lsof_volume
+bin_PROGRAMS = volume_test
+volume_test_SOURCES = \
- volume_test.c
diff --git a/tune/volume/volume_test.c b/tune/volume/volume_test.c new file mode 100644 index 0000000..72fcb02 --- /dev/null +++ b/tune/volume/volume_test.c @@ -0,0 +1,160 @@ +/*
- 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@linux.intel.com
Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
- */
+#include <sof/ipc.h> +#include <getopt.h> +#include "common_test.h" +#include "common_tplg.h"
+#define TESTBENCH_NCH 2 /* Stereo */
+/* main firmware context */ +static struct sof sof; +static int fr_id; /* comp id for fileread */ +static int fw_id; /* comp id for filewrite */ +static int sched_id; /* comp id for scheduling comp */
+static void print_usage(char *executable) +{
- printf("Usage: %s -i <input_file> -o <output_file> -t
<tplg_file> -b <input_format>\n",
executable);
- printf("input_format should be s16le, s32le, s42le or
float\n");
s42 ? Can we make these the same as ALSA, i.e. S32_LE
OK. will change these.
Btw, I'm assuming the audio components are all built-in to the binary ? We do need this to be modular in the future so we can open binary modules (as .so shared objects) using dlopen() and alias them.
Yes, the audio components are all built-in. But they are linked at compile time. I will change it to accept user input at run-time.
e.g. sof-test -i file -o file -t file -a vol=vol_avx2,src=super_src
This would mean that the test bench would load all components as .so and overide the default volume and SRC with vol_avx2 and super_src respectively.
+}
+int main(int argc, char **argv) +{
- struct ipc_comp_dev *pcm_dev;
- struct pipeline *p;
- struct sof_ipc_pipe_new *ipc_pipe;
- struct comp_dev *cd;
- struct fileread_comp_data *frcd;
- struct filewrite_comp_data *fwcd;
- char *tplg_file = NULL, *input_file = NULL;
- char *output_file = NULL, *bits_in = NULL;
- clock_t tic, toc;
- double c_realtime, t_exec;
- int fs, n_in, n_out, ret;
- int option = 0;
- /* command line arguments*/
- while ((option = getopt(argc, argv, "i:o:t:b:")) != -1) {
switch (option) {
case 'i':
input_file = malloc(strlen(optarg));
strcpy(input_file, optarg);
strdup() ?
Sure, will change it.
break;
case 'o':
output_file = malloc(strlen(optarg));
strcpy(output_file, optarg);
break;
case 't':
tplg_file = malloc(strlen(optarg));
strcpy(tplg_file, optarg);
break;
case 'b':
bits_in = malloc(strlen(optarg));
strcpy(bits_in, optarg);
break;
default:
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
- }
- if (!bits_in || !tplg_file || !input_file || !output_file)
{
print_usage(argv[0]);
exit(EXIT_FAILURE);
- }
- /* initialize ipc, pipeline and scheduler */
- if (tb_pipeline_setup(&sof) < 0) {
printf("error: pipeline init\n");
exit(EXIT_FAILURE);
- }
- /* parse topology file and create pipeline */
- if (parse_topology(tplg_file, &sof, &fr_id, &fw_id,
&sched_id, bits_in,
input_file, output_file) < 0) {
printf("error: parsing topology\n");
exit(EXIT_FAILURE);
- }
- /* Get pointers to fileread and filewrite */
- pcm_dev = ipc_get_comp(sof.ipc, fw_id);
- fwcd = comp_get_drvdata(pcm_dev->cd);
- pcm_dev = ipc_get_comp(sof.ipc, fr_id);
- frcd = comp_get_drvdata(pcm_dev->cd);
I'd expect the topology DAIs to be converted to file read/write components. i.e. a pipeline like :-
HOST -> B0 -> VOL0 -> B1 -> SSP
Would convert it's source/sink endpoints to file component.
Yes, thats how I've done it here. I've converted a AIF_OUT component into a fileread and a DAI_IN are a filewrite component from topology.
- /* Run pipeline until EOF from fileread */
- pcm_dev = ipc_get_comp(sof.ipc, sched_id);
- p = pcm_dev->cd->pipeline;
- ipc_pipe = &p->ipc_pipe;
- fs = ipc_pipe->deadline * ipc_pipe->frames_per_sched;
- /* set pipeline params and trigger start */
- if (tb_pipeline_start(sof.ipc, TESTBENCH_NCH, bits_in,
ipc_pipe) < 0) {
printf("error: pipeline params\n");
exit(EXIT_FAILURE);
- }
- cd = pcm_dev->cd;
- tb_disable_trace(); /* Reduce print output */
- tic = clock();
- while (frcd->frs.reached_eof == 0)
pipeline_schedule_copy(p, 0);
- /* reset and free pipeline */
- toc = clock();
- tb_enable_trace();
- ret = pipeline_reset(p, cd);
- if (ret < 0) {
printf("error: pipeline reset\n");
exit(EXIT_FAILURE);
- }
- pipeline_free(p);
- n_in = frcd->frs.n;
- n_out = fwcd->fws.n;
- t_exec = (double) (toc - tic) / CLOCKS_PER_SEC;
- c_realtime = (double) n_out / TESTBENCH_NCH / fs / t_exec;
- /* print test summary */
- printf("==============================================\n")
;
- printf(" Test Summary\n");
- printf("==============================================\n")
;
- printf("Output written to file "%s"\n", output_file);
- printf("Input sample count: %d\n", n_in);
- printf("Output sample count: %d\n", n_out);
- printf("Total execution time: %.2f us, %.2f x realtime\n",
1e3 * t_exec, c_realtime);
- return EXIT_SUCCESS;
+}
Sound-open-firmware mailing list Sound-open-firmware@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/sound-open-firmware
On Wed, 2018-05-02 at 21:31 -0700, Ranjani Sridharan wrote:
This set of patches introduces the testbench for audio processing components in SOF. The testbench enables for functional/quality test of the components. The testbench reads in audio samples from a file and write out the processed samples to the output file.
The common directory includes routines for topology parsing and setting up sof ipc, scheduler and processing pipelines. It also includes simpler implementations for SOF features such as tracing. It also includes the fileread/filewrite components that allow reading in or writing out samples from/to a file.
The volume testbench directory includes the testbench for volume component. The volume testbench reads in input samples from a file ,schedules pipeline copy and copies the processed sample to the output file. The output file can be used to perform audio quality tests using matlab/octave. Testbench for other components such as src/eq will be added later.
Ranjani Sridharan (3): [RFC]tune: add common testbench common headers and source files [RFC]tune: add fileread and filewrite components [RFC]tune: add volume testbench
Makefile.am | 2 +- configure.ac | 5 +- 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 +++++++++++ tune/common/fileread.c | 385 ++++++++++++++++++++ tune/common/filewrite.c | 395 +++++++++++++++++++++ tune/volume/Makefile.am | 17 + tune/volume/volume_test.c | 160 +++++++++
Should thee not go under src/test ?
Liam
On Mon, 2018-05-07 at 14:50 +0100, Liam Girdwood wrote:
On Wed, 2018-05-02 at 21:31 -0700, Ranjani Sridharan wrote:
This set of patches introduces the testbench for audio processing components in SOF. The testbench enables for functional/quality test of the components. The testbench reads in audio samples from a file and write out the processed samples to the output file.
The common directory includes routines for topology parsing and setting up sof ipc, scheduler and processing pipelines. It also includes simpler implementations for SOF features such as tracing. It also includes the fileread/filewrite components that allow reading in or writing out samples from/to a file.
The volume testbench directory includes the testbench for volume component. The volume testbench reads in input samples from a file ,schedules pipeline copy and copies the processed sample to the output file. The output file can be used to perform audio quality tests using matlab/octave. Testbench for other components such as src/eq will be added later.
Ranjani Sridharan (3): [RFC]tune: add common testbench common headers and source files [RFC]tune: add fileread and filewrite components [RFC]tune: add volume testbench
Makefile.am | 2 +- configure.ac | 5 +- 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 +++++++++++ tune/common/fileread.c | 385 ++++++++++++++++++++ tune/common/filewrite.c | 395 +++++++++++++++++++++ tune/volume/Makefile.am | 17 + tune/volume/volume_test.c | 160 +++++++++
Should thee not go under src/test ?
Yes, I'll fix that.
Liam _______________________________________________ Sound-open-firmware mailing list Sound-open-firmware@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/sound-open-firmware
participants (3)
-
Liam Girdwood
-
Ranjani Sridharan
-
Seppo Ingalsuo