Skip to content

Commit 98c27ad

Browse files
committed
ALSA: usb-audio: Cap upper limits of buffer/period bytes for implicit fb
In the implicit feedback mode, some parameters are tied between both playback and capture streams. One of the tied parameters is the period size, and this can be a problem if the device has different number of channels to both streams. Assume that an application opens a playback stream that has an implicit feedback from a capture stream, and it allocates up to the max period and buffer size as much as possible. When the capture device supports only more channels than the playback, the minimum period and buffer sizes become larger than the sizes the playback stream took. That is, the minimum size will be over the max size the driver limits, and PCM core sees as if no available configuration is found, returning -EINVAL mercilessly. For avoiding this problem, we have to look through the counter part of audioformat list for each sync ep, and checks the channels. If more channels are found there, we reduce the max period and buffer sizes accordingly. You may wonder that the patch adds only the evaluation of channels between streams, and what about other parameters? Both the format and the rate are tied in the implicit fb mode, hence they are always identical. BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=215792 Fixes: 5a6c3e1 ("ALSA: usb-audio: Add hw constraint for implicit fb sync") Cc: <stable@vger.kernel.org> Link: https://lore.kernel.org/r/20220407211657.15087-1-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de>
1 parent d52eee9 commit 98c27ad

File tree

1 file changed

+87
-2
lines changed

1 file changed

+87
-2
lines changed

sound/usb/pcm.c

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,9 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
659659
#define hwc_debug(fmt, args...) do { } while(0)
660660
#endif
661661

662+
#define MAX_BUFFER_BYTES (1024 * 1024)
663+
#define MAX_PERIOD_BYTES (512 * 1024)
664+
662665
static const struct snd_pcm_hardware snd_usb_hardware =
663666
{
664667
.info = SNDRV_PCM_INFO_MMAP |
@@ -669,9 +672,9 @@ static const struct snd_pcm_hardware snd_usb_hardware =
669672
SNDRV_PCM_INFO_PAUSE,
670673
.channels_min = 1,
671674
.channels_max = 256,
672-
.buffer_bytes_max = 1024 * 1024,
675+
.buffer_bytes_max = MAX_BUFFER_BYTES,
673676
.period_bytes_min = 64,
674-
.period_bytes_max = 512 * 1024,
677+
.period_bytes_max = MAX_PERIOD_BYTES,
675678
.periods_min = 2,
676679
.periods_max = 1024,
677680
};
@@ -971,6 +974,78 @@ static int hw_rule_periods_implicit_fb(struct snd_pcm_hw_params *params,
971974
ep->cur_buffer_periods);
972975
}
973976

977+
/* get the adjusted max buffer (or period) bytes that can fit with the
978+
* paired format for implicit fb
979+
*/
980+
static unsigned int
981+
get_adjusted_max_bytes(struct snd_usb_substream *subs,
982+
struct snd_usb_substream *pair,
983+
struct snd_pcm_hw_params *params,
984+
unsigned int max_bytes,
985+
bool reverse_map)
986+
{
987+
const struct audioformat *fp, *pp;
988+
unsigned int rmax = 0, r;
989+
990+
list_for_each_entry(fp, &subs->fmt_list, list) {
991+
if (!fp->implicit_fb)
992+
continue;
993+
if (!reverse_map &&
994+
!hw_check_valid_format(subs, params, fp))
995+
continue;
996+
list_for_each_entry(pp, &pair->fmt_list, list) {
997+
if (pp->iface != fp->sync_iface ||
998+
pp->altsetting != fp->sync_altsetting ||
999+
pp->ep_idx != fp->sync_ep_idx)
1000+
continue;
1001+
if (reverse_map &&
1002+
!hw_check_valid_format(pair, params, pp))
1003+
break;
1004+
if (!reverse_map && pp->channels > fp->channels)
1005+
r = max_bytes * fp->channels / pp->channels;
1006+
else if (reverse_map && pp->channels < fp->channels)
1007+
r = max_bytes * pp->channels / fp->channels;
1008+
else
1009+
r = max_bytes;
1010+
rmax = max(rmax, r);
1011+
break;
1012+
}
1013+
}
1014+
return rmax;
1015+
}
1016+
1017+
/* Reduce the period or buffer bytes depending on the paired substream;
1018+
* when a paired configuration for implicit fb has a higher number of channels,
1019+
* we need to reduce the max size accordingly, otherwise it may become unusable
1020+
*/
1021+
static int hw_rule_bytes_implicit_fb(struct snd_pcm_hw_params *params,
1022+
struct snd_pcm_hw_rule *rule)
1023+
{
1024+
struct snd_usb_substream *subs = rule->private;
1025+
struct snd_usb_substream *pair;
1026+
struct snd_interval *it;
1027+
unsigned int max_bytes;
1028+
unsigned int rmax;
1029+
1030+
pair = &subs->stream->substream[!subs->direction];
1031+
if (!pair->ep_num)
1032+
return 0;
1033+
1034+
if (rule->var == SNDRV_PCM_HW_PARAM_PERIOD_BYTES)
1035+
max_bytes = MAX_PERIOD_BYTES;
1036+
else
1037+
max_bytes = MAX_BUFFER_BYTES;
1038+
1039+
rmax = get_adjusted_max_bytes(subs, pair, params, max_bytes, false);
1040+
if (!rmax)
1041+
rmax = get_adjusted_max_bytes(pair, subs, params, max_bytes, true);
1042+
if (!rmax)
1043+
return 0;
1044+
1045+
it = hw_param_interval(params, rule->var);
1046+
return apply_hw_params_minmax(it, 0, rmax);
1047+
}
1048+
9741049
/*
9751050
* set up the runtime hardware information.
9761051
*/
@@ -1085,6 +1160,16 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
10851160
SNDRV_PCM_HW_PARAM_PERIODS, -1);
10861161
if (err < 0)
10871162
return err;
1163+
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
1164+
hw_rule_bytes_implicit_fb, subs,
1165+
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1);
1166+
if (err < 0)
1167+
return err;
1168+
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
1169+
hw_rule_bytes_implicit_fb, subs,
1170+
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -1);
1171+
if (err < 0)
1172+
return err;
10881173

10891174
list_for_each_entry(fp, &subs->fmt_list, list) {
10901175
if (fp->implicit_fb) {

0 commit comments

Comments
 (0)