ASoC: wm_adsp: Detach compressed stream on free
authorCharles Keepax <ckeepax@opensource.wolfsonmicro.com>
Wed, 4 May 2016 16:11:56 +0000 (17:11 +0100)
committerMark Brown <broonie@kernel.org>
Wed, 4 May 2016 16:51:31 +0000 (17:51 +0100)
If someone powers down the DSP core (through routing changes
say) whilst a compressed record is in progress we can end up
using a freed pointer to the buffer object. When a compressed
audio stream is triggered we attach it to a buffer on a physical
DSP. This patch adds a detach of the buffer from the stream when
the stream is freed or when the DSP is powered down which avoids
the situation where we use a buffer when it is no longer valid.

Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/wm_adsp.c

index 42fc469004009373f2fb794093e5e3e216aaf212..a07bd7c2c587dd9d9d0135015b1aa4b578307e93 100644 (file)
@@ -273,8 +273,11 @@ struct wm_adsp_buffer {
        __be32 words_written[2];        /* total words written (64 bit) */
 };
 
+struct wm_adsp_compr;
+
 struct wm_adsp_compr_buf {
        struct wm_adsp *dsp;
+       struct wm_adsp_compr *compr;
 
        struct wm_adsp_buffer_region *regions;
        u32 host_buf_ptr;
@@ -2467,10 +2470,26 @@ static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
                return -EINVAL;
 
        compr->buf = compr->dsp->buffer;
+       compr->buf->compr = compr;
 
        return 0;
 }
 
+static void wm_adsp_compr_detach(struct wm_adsp_compr *compr)
+{
+       if (!compr)
+               return;
+
+       /* Wake the poll so it can see buffer is no longer attached */
+       if (compr->stream)
+               snd_compr_fragment_elapsed(compr->stream);
+
+       if (wm_adsp_compr_attached(compr)) {
+               compr->buf->compr = NULL;
+               compr->buf = NULL;
+       }
+}
+
 int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
 {
        struct wm_adsp_compr *compr;
@@ -2524,6 +2543,7 @@ int wm_adsp_compr_free(struct snd_compr_stream *stream)
 
        mutex_lock(&dsp->pwr_lock);
 
+       wm_adsp_compr_detach(compr);
        dsp->compr = NULL;
 
        kfree(compr->raw_buf);
@@ -2820,6 +2840,8 @@ err_buffer:
 static int wm_adsp_buffer_free(struct wm_adsp *dsp)
 {
        if (dsp->buffer) {
+               wm_adsp_compr_detach(dsp->buffer->compr);
+
                kfree(dsp->buffer->regions);
                kfree(dsp->buffer);