ASoC: soc-pcm: dpcm: Only allow playback/capture if supported
authorStephan Gerhold <stephan@gerhold.net>
Wed, 15 Apr 2020 10:49:28 +0000 (12:49 +0200)
committerMark Brown <broonie@kernel.org>
Wed, 15 Apr 2020 12:00:28 +0000 (13:00 +0100)
At the moment, PCM devices for DPCM are only created based on the
dpcm_playback/capture parameters of the DAI link, without considering
if the CPU/FE DAI is actually capable of playback/capture.

Normally the dpcm_playback/capture parameter should match the
capabilities of the CPU DAI. However, there is no way to set that
parameter from the device tree (e.g. with simple-audio-card or
qcom sound cards). dpcm_playback/capture are always both set to 1.

This causes problems when the CPU DAI does only support playback
or capture. Attemting to open that PCM device with an unsupported
stream type then results in a null pointer dereference:

    Unable to handle kernel NULL pointer dereference at virtual address 0000000000000128
    Internal error: Oops: 96000044 [#1] PREEMPT SMP
    CPU: 3 PID: 1582 Comm: arecord Not tainted 5.7.0-rc1
    pc : invalidate_paths_ep+0x30/0xe0
    lr : snd_soc_dapm_dai_get_connected_widgets+0x170/0x1a8
    Call trace:
     invalidate_paths_ep+0x30/0xe0
     snd_soc_dapm_dai_get_connected_widgets+0x170/0x1a8
     dpcm_path_get+0x38/0xd0
     dpcm_fe_dai_open+0x70/0x920
     snd_pcm_open_substream+0x564/0x840
     snd_pcm_open+0xfc/0x228
     snd_pcm_capture_open+0x4c/0x78
     snd_open+0xac/0x1a8
     ...

... because the DAI playback/capture_widget is not set in that case.

We could add checks there to fix the problem (maybe we should
anyway), but much easier is to not expose the device as
playback/capture in the first place. Attemting to use that
device would always fail later anyway.

Add checks for snd_soc_dai_stream_valid() to the DPCM case
to avoid exposing playback/capture if it is not supported.

Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
Link: https://lore.kernel.org/r/20200415104928.86091-1-stephan@gerhold.net
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/soc-pcm.c

index 289aebc1552935b273463f579f3d4a865a4f409f..1f302de4405259afc43ef17d244975de4addd229 100644 (file)
@@ -2911,8 +2911,17 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
        int i;
 
        if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
-               playback = rtd->dai_link->dpcm_playback;
-               capture = rtd->dai_link->dpcm_capture;
+               cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+               if (rtd->num_cpus > 1) {
+                       dev_err(rtd->dev,
+                               "DPCM doesn't support Multi CPU yet\n");
+                       return -EINVAL;
+               }
+
+               playback = rtd->dai_link->dpcm_playback &&
+                          snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_PLAYBACK);
+               capture = rtd->dai_link->dpcm_capture &&
+                         snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_CAPTURE);
        } else {
                /* Adapt stream for codec2codec links */
                int cpu_capture = rtd->dai_link->params ?