diff --git a/src/include/ipc/topology.h b/src/include/ipc/topology.h index c304824f9..5d072729d 100644 --- a/src/include/ipc/topology.h +++ b/src/include/ipc/topology.h @@ -245,6 +245,7 @@ struct sof_ipc_comp_file { char *fn; uint32_t mode; uint32_t frame_fmt; + uint32_t direction; /**< SOF_IPC_STREAM_ */ } __attribute__((packed, aligned(4))); /* frees components, buffers and pipelines diff --git a/src/include/sof/audio/ipc-config.h b/src/include/sof/audio/ipc-config.h index 9924734c4..07636e7b1 100644 --- a/src/include/sof/audio/ipc-config.h +++ b/src/include/sof/audio/ipc-config.h @@ -98,6 +98,7 @@ struct ipc_comp_file { char *fn; uint32_t mode; uint32_t frame_fmt; + uint32_t direction; /**< SOF_IPC_STREAM_ */ } __attribute__((packed, aligned(4))); #endif diff --git a/src/ipc/ipc3/helper.c b/src/ipc/ipc3/helper.c index 3a3e44de8..af0b621ae 100644 --- a/src/ipc/ipc3/helper.c +++ b/src/ipc/ipc3/helper.c @@ -217,6 +217,7 @@ static void comp_specific_builder(struct sof_ipc_comp *comp, config->file.frame_fmt = file->frame_fmt; config->file.mode = file->mode; config->file.rate = file->rate; + config->file.direction = file->direction; break; #else case SOF_COMP_HOST: diff --git a/tools/testbench/common_test.c b/tools/testbench/common_test.c index 3bf0cda91..78808c845 100644 --- a/tools/testbench/common_test.c +++ b/tools/testbench/common_test.c @@ -97,32 +97,26 @@ void tb_free(struct sof *sof) free(sof->ipc); } -/* set up pcm params, prepare and trigger pipeline */ -int tb_pipeline_start(struct ipc *ipc, struct pipeline *p, - struct tplg_context *ctx) +/* Get pipeline host component */ +static struct comp_dev *tb_get_pipeline_host(struct pipeline *p) +{ + struct comp_dev *cd; + + cd = p->source_comp; + if (cd->direction == SOF_IPC_STREAM_CAPTURE) + cd = p->sink_comp; + + return cd; +} + +/* set up pcm params, prepare and trigger pipeline */ +int tb_pipeline_start(struct ipc *ipc, struct pipeline *p) { - struct ipc_comp_dev *pcm_dev; struct comp_dev *cd; int ret; - /* set up pipeline params */ - ret = tb_pipeline_params(ipc, p, ctx); - if (ret < 0) { - fprintf(stderr, "error: pipeline params failed: %s\n", - strerror(ret)); - return ret; - } - - /* Get IPC component device for pipeline */ - pcm_dev = ipc_get_comp_by_id(ipc, p->sched_id); - if (!pcm_dev) { - fprintf(stderr, "error: ipc get comp failed: %s\n", - strerror(ret)); - return ret; - } - - /* Point to pipeline component device */ - cd = pcm_dev->cd; + /* Get pipeline host component */ + cd = tb_get_pipeline_host(p); /* Component prepare */ ret = pipeline_prepare(p, cd); @@ -133,7 +127,7 @@ int tb_pipeline_start(struct ipc *ipc, struct pipeline *p, } /* Start the pipeline */ - ret = pipeline_trigger(p, cd, COMP_TRIGGER_PRE_START); + ret = pipeline_trigger(cd->pipeline, cd, COMP_TRIGGER_PRE_START); if (ret < 0) { fprintf(stderr, "error: Failed to start pipeline command: %s\n", strerror(ret)); @@ -144,24 +138,15 @@ int tb_pipeline_start(struct ipc *ipc, struct pipeline *p, } /* set up pcm params, prepare and trigger pipeline */ -int tb_pipeline_stop(struct ipc *ipc, struct pipeline *p, - struct tplg_context *ctx) +int tb_pipeline_stop(struct ipc *ipc, struct pipeline *p) { - struct ipc_comp_dev *pcm_dev; struct comp_dev *cd; int ret; - /* Get IPC component device for pipeline */ - pcm_dev = ipc_get_comp_by_id(ipc, p->sched_id); - if (!pcm_dev) { - fprintf(stderr, "error: ipc get comp failed\n"); - return -EINVAL; - } + /* Get pipeline host component */ + cd = tb_get_pipeline_host(p); - /* Point to pipeline component device */ - cd = pcm_dev->cd; - - ret = pipeline_trigger(p, cd, COMP_TRIGGER_STOP); + ret = pipeline_trigger(cd->pipeline, cd, COMP_TRIGGER_STOP); if (ret < 0) { fprintf(stderr, "error: Failed to stop pipeline command: %s\n", strerror(ret)); @@ -171,22 +156,13 @@ int tb_pipeline_stop(struct ipc *ipc, struct pipeline *p, } /* set up pcm params, prepare and trigger pipeline */ -int tb_pipeline_reset(struct ipc *ipc, struct pipeline *p, - struct tplg_context *ctx) +int tb_pipeline_reset(struct ipc *ipc, struct pipeline *p) { - struct ipc_comp_dev *pcm_dev; struct comp_dev *cd; int ret; - /* Get IPC component device for pipeline */ - pcm_dev = ipc_get_comp_by_id(ipc, p->sched_id); - if (!pcm_dev) { - fprintf(stderr, "error: ipc get comp failed\n"); - return -EINVAL; - } - - /* Point to pipeline component device */ - cd = pcm_dev->cd; + /* Get pipeline host component */ + cd = tb_get_pipeline_host(p); ret = pipeline_reset(p, cd); if (ret < 0) @@ -223,7 +199,6 @@ int tb_pipeline_params(struct ipc *ipc, struct pipeline *p, params.comp_id = p->comp_id; params.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; params.params.frame_fmt = ctx->frame_fmt; - params.params.direction = SOF_IPC_STREAM_PLAYBACK; params.params.rate = ctx->fs_in; params.params.channels = ctx->channels_in; @@ -255,8 +230,11 @@ int tb_pipeline_params(struct ipc *ipc, struct pipeline *p, return -EINVAL; } - /* point to pipeline component device */ - cd = pcm_dev->cd; + /* Get pipeline host component */ + cd = tb_get_pipeline_host(p); + + /* Set pipeline params direction from scheduling component */ + params.params.direction = cd->direction; /* pipeline params */ ret = pipeline_params(p, cd, ¶ms); diff --git a/tools/testbench/file.c b/tools/testbench/file.c index bbe222087..5808c7d4f 100644 --- a/tools/testbench/file.c +++ b/tools/testbench/file.c @@ -587,6 +587,7 @@ static struct comp_dev *file_new(const struct comp_driver *drv, cd->rate = ipc_file->rate; cd->channels = ipc_file->channels; cd->frame_fmt = ipc_file->frame_fmt; + dev->direction = ipc_file->direction; /* open file handle(s) depending on mode */ switch (cd->fs.mode) { diff --git a/tools/testbench/include/testbench/common_test.h b/tools/testbench/include/testbench/common_test.h index 2a0c3e4f3..0241df4f5 100644 --- a/tools/testbench/include/testbench/common_test.h +++ b/tools/testbench/include/testbench/common_test.h @@ -21,6 +21,7 @@ #define DEBUG_MSG_LEN 1024 #define MAX_LIB_NAME_LEN 1024 +#define MAX_INPUT_FILE_NUM 16 #define MAX_OUTPUT_FILE_NUM 16 /* number of widgets types supported in testbench */ @@ -36,8 +37,9 @@ struct tplg_context; */ struct testbench_prm { char *tplg_file; /* topology file to use */ - char *input_file; /* input file name */ + char *input_file[MAX_INPUT_FILE_NUM]; /* input file names */ char *output_file[MAX_OUTPUT_FILE_NUM]; /* output file names */ + int input_file_num; /* number of input files */ int output_file_num; /* number of output files */ char *bits_in; /* input bit format */ int pipelines[MAX_OUTPUT_FILE_NUM]; /* output file names */ @@ -58,6 +60,7 @@ struct testbench_prm { FILE *file; char *pipeline_string; int output_file_index; + int input_file_index; /* global cmd line args that can override topology */ enum sof_ipc_frame cmd_frame_fmt; @@ -89,17 +92,14 @@ void sys_comp_filewrite_init(void); int tb_setup(struct sof *sof, struct testbench_prm *tp); void tb_free(struct sof *sof); -int tb_pipeline_start(struct ipc *ipc, struct pipeline *p, - struct tplg_context *ctx); +int tb_pipeline_start(struct ipc *ipc, struct pipeline *p); int tb_pipeline_params(struct ipc *ipc, struct pipeline *p, struct tplg_context *ctx); -int tb_pipeline_stop(struct ipc *ipc, struct pipeline *p, - struct tplg_context *ctx); +int tb_pipeline_stop(struct ipc *ipc, struct pipeline *p); -int tb_pipeline_reset(struct ipc *ipc, struct pipeline *p, - struct tplg_context *ctx); +int tb_pipeline_reset(struct ipc *ipc, struct pipeline *p); void debug_print(char *message); diff --git a/tools/testbench/testbench.c b/tools/testbench/testbench.c index a5c5455e0..24abbce26 100644 --- a/tools/testbench/testbench.c +++ b/tools/testbench/testbench.c @@ -117,6 +117,36 @@ static int parse_output_files(char *outputs, struct testbench_prm *tp) return 0; } +/* + * Parse inputfilenames from user input + */ +static int parse_input_files(char *inputs, struct testbench_prm *tp) +{ + char *input_token = NULL; + char *token = strtok_r(inputs, ",", &input_token); + int index; + + for (index = 0; index < MAX_INPUT_FILE_NUM && token; index++) { + /* get input file name with current index */ + tp->input_file[index] = strdup(token); + + /* next input */ + token = strtok_r(NULL, ",", &input_token); + } + + if (index == MAX_INPUT_FILE_NUM && token) { + fprintf(stderr, "error: max input file number is %d\n", + MAX_INPUT_FILE_NUM); + for (index = 0; index < MAX_INPUT_FILE_NUM; index++) + free(tp->input_file[index]); + return -EINVAL; + } + + /* set total input file number */ + tp->input_file_num = index; + return 0; +} + static int parse_pipelines(char *pipelines, struct testbench_prm *tp) { char *output_token = NULL; @@ -355,7 +385,7 @@ static int parse_input_args(int argc, char **argv, struct testbench_prm *tp) switch (option) { /* input sample file */ case 'i': - tp->input_file = strdup(optarg); + ret = parse_input_files(optarg, tp); break; /* output sample files */ @@ -456,81 +486,143 @@ static int parse_input_args(int argc, char **argv, struct testbench_prm *tp) return ret; } -static int test_pipeline_stop(struct pipeline_thread_data *ptdata, - struct tplg_context *ctx) +static struct pipeline *get_pipeline_by_id(int id) { struct ipc_comp_dev *pcm_dev; - struct pipeline *p; + struct ipc *ipc = sof_get()->ipc; - pcm_dev = ipc_get_comp_by_id(sof_get()->ipc, ctx->sched_id); - p = pcm_dev->cd->pipeline; - - return tb_pipeline_stop(sof_get()->ipc, p, ctx); + pcm_dev = ipc_get_ppl_src_comp(ipc, id); + return pcm_dev->cd->pipeline; } -static int test_pipeline_reset(struct pipeline_thread_data *ptdata, - struct tplg_context *ctx) +static int test_pipeline_stop(struct pipeline_thread_data *ptdata) { - struct ipc_comp_dev *pcm_dev; + struct testbench_prm *tp = ptdata->tp; struct pipeline *p; + struct ipc *ipc = sof_get()->ipc; + int ret = 0; + int i; - pcm_dev = ipc_get_comp_by_id(sof_get()->ipc, ctx->sched_id); - p = pcm_dev->cd->pipeline; + for (i = 0; i < tp->pipeline_num; i++) { + p = get_pipeline_by_id(tp->pipelines[i]); + ret = tb_pipeline_stop(ipc, p); + if (ret < 0) + break; + } - return tb_pipeline_reset(sof_get()->ipc, p, ctx); + return ret; } -static int test_pipeline_start(struct pipeline_thread_data *ptdata, - struct tplg_context *ctx) +static int test_pipeline_reset(struct pipeline_thread_data *ptdata) +{ + struct testbench_prm *tp = ptdata->tp; + struct pipeline *p; + struct ipc *ipc = sof_get()->ipc; + int ret = 0; + int i; + + for (i = 0; i < tp->pipeline_num; i++) { + p = get_pipeline_by_id(tp->pipelines[i]); + ret = tb_pipeline_reset(ipc, p); + if (ret < 0) + break; + } + + return ret; +} + +static void test_pipeline_free(struct pipeline_thread_data *ptdata) +{ + struct testbench_prm *tp = ptdata->tp; + int i; + + for (i = 0; i < tp->pipeline_num; i++) + test_pipeline_free_comps(tp->pipelines[i]); +} + +static int test_pipeline_params(struct pipeline_thread_data *ptdata, struct tplg_context *ctx) { struct testbench_prm *tp = ptdata->tp; struct ipc_comp_dev *pcm_dev; struct pipeline *p; + struct ipc *ipc = sof_get()->ipc; + int ret = 0; + int i; /* Run pipeline until EOF from fileread */ - pcm_dev = ipc_get_comp_by_id(sof_get()->ipc, ctx->sched_id); - if (!pcm_dev) { - fprintf(stderr, "error: pipeline has no scheduling component %d\n", - ctx->sched_id); - return -EINVAL; + + for (i = 0; i < tp->pipeline_num; i++) { + pcm_dev = ipc_get_ppl_src_comp(ipc, tp->pipelines[i]); + if (!pcm_dev) { + fprintf(stderr, "error: pipeline %d has no source component\n", + tp->pipelines[i]); + return -EINVAL; + } + + /* set up pipeline params */ + p = pcm_dev->cd->pipeline; + + /* input and output sample rate */ + if (!ctx->fs_in) + ctx->fs_in = p->period * p->frames_per_sched; + + if (!ctx->fs_out) + ctx->fs_out = p->period * p->frames_per_sched; + + ret = tb_pipeline_params(ipc, p, ctx); + if (ret < 0) { + fprintf(stderr, "error: pipeline params failed: %s\n", + strerror(ret)); + return ret; + } } - p = pcm_dev->cd->pipeline; - /* input and output sample rate */ - if (!ctx->fs_in) - ctx->fs_in = p->period * p->frames_per_sched; + return 0; +} - if (!ctx->fs_out) - ctx->fs_out = p->period * p->frames_per_sched; +static int test_pipeline_start(struct pipeline_thread_data *ptdata) +{ + struct testbench_prm *tp = ptdata->tp; + struct pipeline *p; + struct ipc *ipc = sof_get()->ipc; + int i; - /* do we need to apply copy count limit ? */ - if (tp->copy_check) - test_pipeline_set_test_limits(ctx->pipeline_id, tp->copy_iterations, 0); + /* Run pipeline until EOF from fileread */ + for (i = 0; i < tp->pipeline_num; i++) { + p = get_pipeline_by_id(tp->pipelines[i]); - /* set pipeline params and trigger start */ - if (tb_pipeline_start(sof_get()->ipc, p, ctx) < 0) { - fprintf(stderr, "error: pipeline params\n"); - return -EINVAL; + /* do we need to apply copy count limit ? */ + if (tp->copy_check) + test_pipeline_set_test_limits(tp->pipelines[i], tp->copy_iterations, 0); + + /* set pipeline params and trigger start */ + if (tb_pipeline_start(ipc, p) < 0) { + fprintf(stderr, "error: pipeline params\n"); + return -EINVAL; + } } return 0; } -static int test_pipeline_get_state(struct pipeline_thread_data *ptdata, - struct tplg_context *ctx) +static bool test_pipeline_check_state(struct pipeline_thread_data *ptdata, int state) { - struct ipc_comp_dev *pcm_dev; + struct testbench_prm *tp = ptdata->tp; struct pipeline *p; + int i; /* Run pipeline until EOF from fileread */ - pcm_dev = ipc_get_comp_by_id(sof_get()->ipc, ctx->sched_id); - p = pcm_dev->cd->pipeline; - return p->pipe_task->state; + for (i = 0; i < tp->pipeline_num; i++) { + p = get_pipeline_by_id(tp->pipelines[i]); + if (p->pipe_task->state == state) + return true; + } + + return false; } -static int test_pipeline_load(struct pipeline_thread_data *ptdata, - struct tplg_context *ctx, int pipeline_id) +static int test_pipeline_load(struct pipeline_thread_data *ptdata, struct tplg_context *ctx) { struct testbench_prm *tp = ptdata->tp; int ret; @@ -543,7 +635,6 @@ static int test_pipeline_load(struct pipeline_thread_data *ptdata, ctx->sof = sof_get(); ctx->tp = tp; ctx->tplg_file = tp->tplg_file; - ctx->pipeline_id = pipeline_id; ctx->fs_in = tp->cmd_fs_in; ctx->fs_out = tp->cmd_fs_out; ctx->channels_in = tp->cmd_channels_in; @@ -558,12 +649,6 @@ static int test_pipeline_load(struct pipeline_thread_data *ptdata, return ret; } -static void test_pipeline_free(struct pipeline_thread_data *ptdata, - struct tplg_context *ctx, int pipeline_id) -{ - test_pipeline_free_comps(pipeline_id); -} - static void test_pipeline_stats(struct pipeline_thread_data *ptdata, struct tplg_context *ctx, uint64_t delta) { @@ -622,6 +707,10 @@ static void test_pipeline_stats(struct pipeline_thread_data *ptdata, printf("Input bit format: %s\n", tp->bits_in); printf("Input sample rate: %d\n", ctx->fs_in); printf("Output sample rate: %d\n", ctx->fs_out); + for (i = 0; i < tp->input_file_num; i++) { + printf("Input[%d] read from file: \"%s\"\n", + i, tp->input_file[i]); + } for (i = 0; i < tp->output_file_num; i++) { printf("Output[%d] written to file: \"%s\"\n", i, tp->output_file[i]); @@ -659,14 +748,21 @@ static void *pipline_test(void *data) printf(" Test Start %d\n", dp_count); printf("==========================================================\n"); - err = test_pipeline_load(ptdata, &ctx, tp->pipelines[0]); + err = test_pipeline_load(ptdata, &ctx); if (err < 0) { fprintf(stderr, "error: pipeline load %d failed %d\n", dp_count, err); break; } - err = test_pipeline_start(ptdata, &ctx); + err = test_pipeline_params(ptdata, &ctx); + if (err < 0) { + fprintf(stderr, "error: pipeline params %d failed %d\n", + dp_count, err); + break; + } + + err = test_pipeline_start(ptdata); if (err < 0) { fprintf(stderr, "error: pipeline run %d failed %d\n", dp_count, err); @@ -691,8 +787,7 @@ static void *pipline_test(void *data) err = nanosleep(&ts, &ts); if (err == 0) { nsleep_time += tp->tick_period_us; /* sleep fully completed */ - if (test_pipeline_get_state(ptdata, &ctx) == - SOF_TASK_STATE_CANCEL) { + if (test_pipeline_check_state(ptdata, SOF_TASK_STATE_CANCEL)) { fprintf(stdout, "pipeline cancelled !\n"); break; } @@ -707,7 +802,7 @@ static void *pipline_test(void *data) } clock_gettime(CLOCK_MONOTONIC, &td1); - err = test_pipeline_stop(ptdata, &ctx); + err = test_pipeline_stop(ptdata); if (err < 0) { fprintf(stderr, "error: pipeline stop %d failed %d\n", dp_count, err); @@ -718,14 +813,14 @@ static void *pipline_test(void *data) delta += (td1.tv_nsec - td0.tv_nsec) / 1000; test_pipeline_stats(ptdata, &ctx, delta); - err = test_pipeline_reset(ptdata, &ctx); + err = test_pipeline_reset(ptdata); if (err < 0) { fprintf(stderr, "error: pipeline stop %d failed %d\n", dp_count, err); break; } - test_pipeline_free(ptdata, &ctx, tp->pipelines[0]); + test_pipeline_free(ptdata); ptdata->count++; dp_count++; @@ -746,12 +841,15 @@ int main(int argc, char **argv) tp.cmd_fs_in = 0; tp.cmd_fs_out = 0; tp.bits_in = 0; - tp.input_file = NULL; tp.tplg_file = NULL; + tp.input_file_num = 0; tp.output_file_num = 0; for (i = 0; i < MAX_OUTPUT_FILE_NUM; i++) tp.output_file[i] = NULL; + for (i = 0; i < MAX_INPUT_FILE_NUM; i++) + tp.input_file[i] = NULL; + tp.cmd_channels_in = TESTBENCH_NCH; tp.cmd_channels_out = 0; tp.max_pipeline_id = 0; @@ -781,8 +879,8 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } - if (!tp.input_file) { - fprintf(stderr, "input audio file not specified, use -i file\n"); + if (!tp.input_file_num) { + fprintf(stderr, "input files not specified, use -i file1,file2\n"); print_usage(argv[0]); exit(EXIT_FAILURE); } @@ -844,10 +942,13 @@ join: out: /* free all other data */ free(tp.bits_in); - free(tp.input_file); free(tp.tplg_file); for (i = 0; i < tp.output_file_num; i++) free(tp.output_file[i]); + + for (i = 0; i < tp.input_file_num; i++) + free(tp.input_file[i]); + free(tp.pipeline_string); #ifdef TESTBENCH_CACHE_CHECK diff --git a/tools/testbench/topology.c b/tools/testbench/topology.c index ba6d79085..f69d57728 100644 --- a/tools/testbench/topology.c +++ b/tools/testbench/topology.c @@ -245,16 +245,19 @@ static int load_fileread(struct tplg_context *ctx, int dir) } /* configure fileread */ - fileread.fn = strdup(tp->input_file); + fileread.fn = strdup(tp->input_file[tp->input_file_index]); + if (tp->input_file_index == 0) + tp->fr_id = ctx->comp_id; /* use fileread comp as scheduling comp */ - tp->fr_id = ctx->comp_id; ctx->sched_id = ctx->comp_id; + tp->input_file_index++; /* Set format from testbench command line*/ fileread.rate = ctx->fs_in; fileread.channels = ctx->channels_in; fileread.frame_fmt = ctx->frame_fmt; + fileread.direction = dir; /* Set type depending on direction */ fileread.comp.type = (dir == SOF_IPC_STREAM_PLAYBACK) ? @@ -304,6 +307,7 @@ static int load_filewrite(struct tplg_context *ctx, int dir) filewrite.rate = ctx->fs_out; filewrite.channels = ctx->channels_out; filewrite.frame_fmt = ctx->frame_fmt; + filewrite.direction = dir; /* Set type depending on direction */ filewrite.comp.type = (dir == SOF_IPC_STREAM_PLAYBACK) ? @@ -361,6 +365,7 @@ int parse_topology(struct tplg_context *ctx) int ret = 0; size_t file_size; size_t size; + bool pipeline_match; /* initialize output file index */ tp->output_file_index = 0; @@ -410,7 +415,17 @@ int parse_topology(struct tplg_context *ctx) ctx->hdr = hdr; - if (hdr->index != ctx->pipeline_id) { + pipeline_match = false; + for (i = 0; i < tp->pipeline_num; i++) { + if (hdr->index == tp->pipelines[i]) { + pipeline_match = true; + break; + } + } + + if (!pipeline_match) { + sprintf(message, "skipped pipeline %d\n", hdr->index); + debug_print(message); next = get_next_hdr(ctx, hdr, file_size); if (next < 0) goto out; @@ -418,6 +433,7 @@ int parse_topology(struct tplg_context *ctx) continue; else goto finish; + } /* parse header and load the next block based on type */ @@ -431,6 +447,7 @@ int parse_topology(struct tplg_context *ctx) debug_print(message); /* update max pipeline_id */ + ctx->pipeline_id = hdr->index; if (hdr->index > tp->max_pipeline_id) tp->max_pipeline_id = hdr->index;