@@ -188,7 +188,7 @@ static inline void loopback_timer_stop(struct loopback_pcm *dpcm)
188
188
189
189
static int loopback_check_format (struct loopback_cable * cable , int stream )
190
190
{
191
- struct snd_pcm_runtime * runtime ;
191
+ struct snd_pcm_runtime * runtime , * cruntime ;
192
192
struct loopback_setup * setup ;
193
193
struct snd_card * card ;
194
194
int check ;
@@ -200,11 +200,11 @@ static int loopback_check_format(struct loopback_cable *cable, int stream)
200
200
}
201
201
runtime = cable -> streams [SNDRV_PCM_STREAM_PLAYBACK ]->
202
202
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 ;
208
208
if (!check )
209
209
return 0 ;
210
210
if (stream == SNDRV_PCM_STREAM_CAPTURE ) {
@@ -274,12 +274,42 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
274
274
return 0 ;
275
275
}
276
276
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
+
277
307
static int loopback_prepare (struct snd_pcm_substream * substream )
278
308
{
279
309
struct snd_pcm_runtime * runtime = substream -> runtime ;
280
310
struct loopback_pcm * dpcm = runtime -> private_data ;
281
311
struct loopback_cable * cable = dpcm -> cable ;
282
- unsigned int bps , salign ;
312
+ int bps , salign ;
283
313
284
314
salign = (snd_pcm_format_width (runtime -> format ) *
285
315
runtime -> channels ) / 8 ;
@@ -303,13 +333,10 @@ static int loopback_prepare(struct snd_pcm_substream *substream)
303
333
dpcm -> pcm_period_size = frames_to_bytes (runtime , runtime -> period_size );
304
334
305
335
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 );
313
340
cable -> valid |= 1 << substream -> stream ;
314
341
mutex_unlock (& dpcm -> loopback -> cable_lock );
315
342
@@ -542,6 +569,47 @@ static unsigned int get_cable_index(struct snd_pcm_substream *substream)
542
569
return !substream -> stream ;
543
570
}
544
571
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
+
545
613
static int loopback_open (struct snd_pcm_substream * substream )
546
614
{
547
615
struct snd_pcm_runtime * runtime = substream -> runtime ;
@@ -579,14 +647,34 @@ static int loopback_open(struct snd_pcm_substream *substream)
579
647
580
648
snd_pcm_hw_constraint_integer (runtime , SNDRV_PCM_HW_PARAM_PERIODS );
581
649
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
+
582
672
runtime -> private_data = dpcm ;
583
673
runtime -> private_free = loopback_runtime_free ;
584
- if (get_notify (dpcm ) &&
585
- substream -> stream == SNDRV_PCM_STREAM_PLAYBACK ) {
674
+ if (get_notify (dpcm ))
586
675
runtime -> hw = loopback_pcm_hardware ;
587
- } else {
676
+ else
588
677
runtime -> hw = cable -> hw ;
589
- }
590
678
unlock :
591
679
mutex_unlock (& loopback -> cable_lock );
592
680
return err ;
0 commit comments