diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index c083b223df85..74cfd9119ccf 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -322,56 +322,75 @@ static int loopback_snd_timer_close_cable(struct loopback_pcm *dpcm) static int loopback_check_format(struct loopback_cable *cable, int stream) { + struct loopback_pcm *dpcm_play, *dpcm_capt; struct snd_pcm_runtime *runtime, *cruntime; struct loopback_setup *setup; struct snd_card *card; - int check; + unsigned long flags; + bool stop_capture = false; + int check, err = 0; + + spin_lock_irqsave(&cable->lock, flags); + dpcm_play = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; + dpcm_capt = cable->streams[SNDRV_PCM_STREAM_CAPTURE]; if (cable->valid != CABLE_VALID_BOTH) { - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - goto __notify; - return 0; - } - runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]-> - substream->runtime; - cruntime = cable->streams[SNDRV_PCM_STREAM_CAPTURE]-> - substream->runtime; - check = runtime->format != cruntime->format || - runtime->rate != cruntime->rate || - runtime->channels != cruntime->channels; - if (!check) - return 0; - if (stream == SNDRV_PCM_STREAM_CAPTURE) { - return -EIO; + if (stream == SNDRV_PCM_STREAM_CAPTURE || !dpcm_play) + goto unlock; } else { - /* close must not free the peer runtime below */ - atomic_inc(&cable->stop_count); - snd_pcm_stop(cable->streams[SNDRV_PCM_STREAM_CAPTURE]-> - substream, SNDRV_PCM_STATE_DRAINING); + if (!dpcm_play || !dpcm_capt) { + err = -EIO; + goto unlock; + } + runtime = dpcm_play->substream->runtime; + cruntime = dpcm_capt->substream->runtime; + if (!runtime || !cruntime) { + err = -EIO; + goto unlock; + } + check = runtime->format != cruntime->format || + runtime->rate != cruntime->rate || + runtime->channels != cruntime->channels; + if (!check) + goto unlock; + if (stream == SNDRV_PCM_STREAM_CAPTURE) { + err = -EIO; + goto unlock; + } else if (snd_pcm_running(dpcm_capt->substream)) { + /* close must not free the peer runtime below */ + atomic_inc(&cable->stop_count); + stop_capture = true; + } + } + + setup = get_setup(dpcm_play); + card = dpcm_play->loopback->card; + runtime = dpcm_play->substream->runtime; + if (setup->format != runtime->format) { + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &setup->format_id); + setup->format = runtime->format; + } + if (setup->rate != runtime->rate) { + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &setup->rate_id); + setup->rate = runtime->rate; + } + if (setup->channels != runtime->channels) { + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &setup->channels_id); + setup->channels = runtime->channels; + } + +unlock: + spin_unlock_irqrestore(&cable->lock, flags); + if (stop_capture) { + snd_pcm_stop(dpcm_capt->substream, SNDRV_PCM_STATE_DRAINING); if (atomic_dec_and_test(&cable->stop_count)) wake_up(&cable->stop_wait); - __notify: - runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]-> - substream->runtime; - setup = get_setup(cable->streams[SNDRV_PCM_STREAM_PLAYBACK]); - card = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->loopback->card; - if (setup->format != runtime->format) { - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &setup->format_id); - setup->format = runtime->format; - } - if (setup->rate != runtime->rate) { - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &setup->rate_id); - setup->rate = runtime->rate; - } - if (setup->channels != runtime->channels) { - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &setup->channels_id); - setup->channels = runtime->channels; - } } - return 0; + + return err; } static void loopback_active_notify(struct loopback_pcm *dpcm)