Skip to content

Commit b1c73fc

Browse files
committed
ALSA: snd-aloop: Fix hw_params restrictions and checking
This patch fixes the hw_params restrictions when first (or playback) stream sets the final hardware parameters. Also, fix the hw_params checking in the trigger callback. Signed-off-by: Jaroslav Kysela <[email protected]>
1 parent ac446fb commit b1c73fc

File tree

1 file changed

+106
-18
lines changed

1 file changed

+106
-18
lines changed

sound/drivers/aloop.c

Lines changed: 106 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ static inline void loopback_timer_stop(struct loopback_pcm *dpcm)
188188

189189
static int loopback_check_format(struct loopback_cable *cable, int stream)
190190
{
191-
struct snd_pcm_runtime *runtime;
191+
struct snd_pcm_runtime *runtime, *cruntime;
192192
struct loopback_setup *setup;
193193
struct snd_card *card;
194194
int check;
@@ -200,11 +200,11 @@ static int loopback_check_format(struct loopback_cable *cable, int stream)
200200
}
201201
runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->
202202
substream->runtime;
203-
check = cable->hw.formats != (1ULL << runtime->format) ||
204-
cable->hw.rate_min != runtime->rate ||
205-
cable->hw.rate_max != runtime->rate ||
206-
cable->hw.channels_min != runtime->channels ||
207-
cable->hw.channels_max != runtime->channels;
203+
cruntime = cable->streams[SNDRV_PCM_STREAM_CAPTURE]->
204+
substream->runtime;
205+
check = runtime->format != cruntime->format ||
206+
runtime->rate != cruntime->rate ||
207+
runtime->channels != cruntime->channels;
208208
if (!check)
209209
return 0;
210210
if (stream == SNDRV_PCM_STREAM_CAPTURE) {
@@ -274,12 +274,42 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
274274
return 0;
275275
}
276276

277+
static void params_change_substream(struct loopback_pcm *dpcm,
278+
struct snd_pcm_runtime *runtime)
279+
{
280+
struct snd_pcm_runtime *dst_runtime;
281+
282+
if (dpcm == NULL || dpcm->substream == NULL)
283+
return;
284+
dst_runtime = dpcm->substream->runtime;
285+
if (dst_runtime == NULL)
286+
return;
287+
dst_runtime->hw = dpcm->cable->hw;
288+
}
289+
290+
static void params_change(struct snd_pcm_substream *substream)
291+
{
292+
struct snd_pcm_runtime *runtime = substream->runtime;
293+
struct loopback_pcm *dpcm = runtime->private_data;
294+
struct loopback_cable *cable = dpcm->cable;
295+
296+
cable->hw.formats = (1ULL << runtime->format);
297+
cable->hw.rate_min = runtime->rate;
298+
cable->hw.rate_max = runtime->rate;
299+
cable->hw.channels_min = runtime->channels;
300+
cable->hw.channels_max = runtime->channels;
301+
params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
302+
runtime);
303+
params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE],
304+
runtime);
305+
}
306+
277307
static int loopback_prepare(struct snd_pcm_substream *substream)
278308
{
279309
struct snd_pcm_runtime *runtime = substream->runtime;
280310
struct loopback_pcm *dpcm = runtime->private_data;
281311
struct loopback_cable *cable = dpcm->cable;
282-
unsigned int bps, salign;
312+
int bps, salign;
283313

284314
salign = (snd_pcm_format_width(runtime->format) *
285315
runtime->channels) / 8;
@@ -303,13 +333,10 @@ static int loopback_prepare(struct snd_pcm_substream *substream)
303333
dpcm->pcm_period_size = frames_to_bytes(runtime, runtime->period_size);
304334

305335
mutex_lock(&dpcm->loopback->cable_lock);
306-
if (!(cable->valid & ~(1 << substream->stream))) {
307-
cable->hw.formats = (1ULL << runtime->format);
308-
cable->hw.rate_min = runtime->rate;
309-
cable->hw.rate_max = runtime->rate;
310-
cable->hw.channels_min = runtime->channels;
311-
cable->hw.channels_max = runtime->channels;
312-
}
336+
if (!(cable->valid & ~(1 << substream->stream)) ||
337+
(get_setup(dpcm)->notify &&
338+
substream->stream == SNDRV_PCM_STREAM_PLAYBACK))
339+
params_change(substream);
313340
cable->valid |= 1 << substream->stream;
314341
mutex_unlock(&dpcm->loopback->cable_lock);
315342

@@ -542,6 +569,47 @@ static unsigned int get_cable_index(struct snd_pcm_substream *substream)
542569
return !substream->stream;
543570
}
544571

572+
static int rule_format(struct snd_pcm_hw_params *params,
573+
struct snd_pcm_hw_rule *rule)
574+
{
575+
576+
struct snd_pcm_hardware *hw = rule->private;
577+
struct snd_mask *maskp = hw_param_mask(params, rule->var);
578+
579+
maskp->bits[0] &= (u_int32_t)hw->formats;
580+
maskp->bits[1] &= (u_int32_t)(hw->formats >> 32);
581+
memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */
582+
if (! maskp->bits[0] && ! maskp->bits[1])
583+
return -EINVAL;
584+
return 0;
585+
}
586+
587+
static int rule_rate(struct snd_pcm_hw_params *params,
588+
struct snd_pcm_hw_rule *rule)
589+
{
590+
struct snd_pcm_hardware *hw = rule->private;
591+
struct snd_interval t;
592+
593+
t.min = hw->rate_min;
594+
t.max = hw->rate_max;
595+
t.openmin = t.openmax = 0;
596+
t.integer = 0;
597+
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
598+
}
599+
600+
static int rule_channels(struct snd_pcm_hw_params *params,
601+
struct snd_pcm_hw_rule *rule)
602+
{
603+
struct snd_pcm_hardware *hw = rule->private;
604+
struct snd_interval t;
605+
606+
t.min = hw->channels_min;
607+
t.max = hw->channels_max;
608+
t.openmin = t.openmax = 0;
609+
t.integer = 0;
610+
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
611+
}
612+
545613
static int loopback_open(struct snd_pcm_substream *substream)
546614
{
547615
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -579,14 +647,34 @@ static int loopback_open(struct snd_pcm_substream *substream)
579647

580648
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
581649

650+
/* use dynamic rules based on actual runtime->hw values */
651+
/* note that the default rules created in the PCM midlevel code */
652+
/* are cached -> they do not reflect the actual state */
653+
err = snd_pcm_hw_rule_add(runtime, 0,
654+
SNDRV_PCM_HW_PARAM_FORMAT,
655+
rule_format, &runtime->hw,
656+
SNDRV_PCM_HW_PARAM_FORMAT, -1);
657+
if (err < 0)
658+
goto unlock;
659+
err = snd_pcm_hw_rule_add(runtime, 0,
660+
SNDRV_PCM_HW_PARAM_RATE,
661+
rule_rate, &runtime->hw,
662+
SNDRV_PCM_HW_PARAM_RATE, -1);
663+
if (err < 0)
664+
goto unlock;
665+
err = snd_pcm_hw_rule_add(runtime, 0,
666+
SNDRV_PCM_HW_PARAM_CHANNELS,
667+
rule_channels, &runtime->hw,
668+
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
669+
if (err < 0)
670+
goto unlock;
671+
582672
runtime->private_data = dpcm;
583673
runtime->private_free = loopback_runtime_free;
584-
if (get_notify(dpcm) &&
585-
substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
674+
if (get_notify(dpcm))
586675
runtime->hw = loopback_pcm_hardware;
587-
} else {
676+
else
588677
runtime->hw = cable->hw;
589-
}
590678
unlock:
591679
mutex_unlock(&loopback->cable_lock);
592680
return err;

0 commit comments

Comments
 (0)