Audio: SRC: Fix incorrect and insufficient buffer size check

This patch checks in prepare() buffers for nominal frames count
(sample rate times schedule period) plus internal input and output
block size constraint. With this size check passed the SRC is able to
process a single block of data per copy() if upstream and downstream
components operate with reasonably small block constraints.

Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
This commit is contained in:
Seppo Ingalsuo 2022-04-04 20:22:40 +03:00 committed by Liam Girdwood
parent 3aff4598f7
commit 40b6c80f18
1 changed files with 50 additions and 25 deletions

View File

@ -909,6 +909,50 @@ static int src_copy(struct comp_dev *dev)
return 0;
}
static int src_check_buffer_sizes(struct comp_data *cd,
struct audio_stream *source_stream,
struct audio_stream *sink_stream)
{
struct src_stage *s1 = cd->src.stage1;
struct src_stage *s2 = cd->src.stage2;
int stage1_times;
int stage2_times;
int blk_in;
int blk_out;
int n;
if (s2->filter_length > 1) {
/* Two polyphase filters case */
stage2_times = ceil_divide(cd->sink_frames, s2->blk_out);
stage1_times = ceil_divide(cd->source_frames, s1->blk_in);
blk_in = stage1_times * s1->blk_in;
blk_out = stage2_times * s2->blk_out;
} else {
/* Single polyphase filter case */
stage1_times = ceil_divide(cd->sink_frames, s1->blk_out);
n = ceil_divide(cd->source_frames, s1->blk_in);
stage1_times = MAX(stage1_times, n);
blk_in = stage1_times * s1->blk_in;
blk_out = stage1_times * s1->blk_out;
}
n = audio_stream_frame_bytes(source_stream) * (blk_in + cd->source_frames);
if (source_stream->size < n) {
comp_cl_err(&comp_src, "Source size %d is less than required %d",
source_stream->size, n);
return -ENOBUFS;
}
n = audio_stream_frame_bytes(sink_stream) * (blk_out + cd->sink_frames);
if (sink_stream->size < n) {
comp_cl_err(&comp_src, "Sink size %d is less than required %d",
sink_stream->size, n);
return -ENOBUFS;
}
return 0;
}
static int src_prepare(struct comp_dev *dev)
{
struct comp_data *cd = comp_get_drvdata(dev);
@ -916,8 +960,6 @@ static int src_prepare(struct comp_dev *dev)
struct comp_buffer *sourceb;
enum sof_ipc_frame source_format;
enum sof_ipc_frame sink_format;
uint32_t source_period_bytes;
uint32_t sink_period_bytes;
int ret;
comp_info(dev, "src_prepare()");
@ -935,33 +977,16 @@ static int src_prepare(struct comp_dev *dev)
sinkb = list_first_item(&dev->bsink_list,
struct comp_buffer, source_list);
/* get source data format and period bytes */
/* get source data format */
source_format = sourceb->stream.frame_fmt;
source_period_bytes = audio_stream_period_bytes(&sourceb->stream,
dev->frames);
/* get sink data format and period bytes */
/* get sink data format */
sink_format = sinkb->stream.frame_fmt;
sink_period_bytes = audio_stream_period_bytes(&sinkb->stream,
dev->frames);
if (sinkb->stream.size < dev->ipc_config.periods_sink * sink_period_bytes) {
comp_err(dev, "src_prepare(): sink buffer size %d is insufficient < %d * %d",
sinkb->stream.size, dev->ipc_config.periods_sink, sink_period_bytes);
ret = -ENOMEM;
goto err;
}
/* validate */
if (!sink_period_bytes) {
comp_err(dev, "src_prepare(): sink_period_bytes = 0");
ret = -EINVAL;
goto err;
}
if (!source_period_bytes) {
comp_err(dev, "src_prepare(): source_period_bytes = 0");
ret = -EINVAL;
goto err;
ret = src_check_buffer_sizes(cd, &sourceb->stream, &sinkb->stream);
if (ret < 0) {
comp_err(dev, "src_prepare(): source or sink buffer size is insufficient");
return ret;
}
/* SRC supports S16_LE, S24_4LE and S32_LE formats */