aboutsummaryrefslogtreecommitdiff
path: root/sound/usb
diff options
context:
space:
mode:
authorTakashi Iwai2021-09-29 10:08:41 +0200
committerTakashi Iwai2021-09-30 13:55:22 +0200
commitd215f63d49da9a8803af3e81acd6cad743686573 (patch)
treebb91dedf94d63f1d82e5f62a44a94396b7233030 /sound/usb
parentbceee75387554f682638e719d1ea60125ea78cea (diff)
ALSA: usb-audio: Check available frames for the next packet size
This is yet more preparation for the upcoming changes. Extend snd_usb_endpoint_next_packet_size() to check the available frames and return -EAGAIN if the next packet size is equal or exceeds the given size. This will be needed for avoiding XRUN during the low latency operation. As of this patch, avail=0 is passed, i.e. the check is skipped and no behavior change. Link: https://lore.kernel.org/r/20210929080844.11583-7-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/endpoint.c51
-rw-r--r--sound/usb/endpoint.h3
-rw-r--r--sound/usb/pcm.c2
3 files changed, 39 insertions, 17 deletions
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 8e164d71d9ac..1f757a7eeafe 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -148,18 +148,23 @@ int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep)
* This won't be used for implicit feedback which takes the packet size
* returned from the sync source
*/
-static int slave_next_packet_size(struct snd_usb_endpoint *ep)
+static int slave_next_packet_size(struct snd_usb_endpoint *ep,
+ unsigned int avail)
{
unsigned long flags;
+ unsigned int phase;
int ret;
if (ep->fill_max)
return ep->maxframesize;
spin_lock_irqsave(&ep->lock, flags);
- ep->phase = (ep->phase & 0xffff)
- + (ep->freqm << ep->datainterval);
- ret = min(ep->phase >> 16, ep->maxframesize);
+ phase = (ep->phase & 0xffff) + (ep->freqm << ep->datainterval);
+ ret = min(phase >> 16, ep->maxframesize);
+ if (avail && ret >= avail)
+ ret = -EAGAIN;
+ else
+ ep->phase = phase;
spin_unlock_irqrestore(&ep->lock, flags);
return ret;
@@ -169,20 +174,25 @@ static int slave_next_packet_size(struct snd_usb_endpoint *ep)
* Return the number of samples to be sent in the next packet
* for adaptive and synchronous endpoints
*/
-static int next_packet_size(struct snd_usb_endpoint *ep)
+static int next_packet_size(struct snd_usb_endpoint *ep, unsigned int avail)
{
+ unsigned int sample_accum;
int ret;
if (ep->fill_max)
return ep->maxframesize;
- ep->sample_accum += ep->sample_rem;
- if (ep->sample_accum >= ep->pps) {
- ep->sample_accum -= ep->pps;
+ sample_accum += ep->sample_rem;
+ if (sample_accum >= ep->pps) {
+ sample_accum -= ep->pps;
ret = ep->packsize[1];
} else {
ret = ep->packsize[0];
}
+ if (avail && ret >= avail)
+ ret = -EAGAIN;
+ else
+ ep->sample_accum = sample_accum;
return ret;
}
@@ -190,16 +200,27 @@ static int next_packet_size(struct snd_usb_endpoint *ep)
/*
* snd_usb_endpoint_next_packet_size: Return the number of samples to be sent
* in the next packet
+ *
+ * If the size is equal or exceeds @avail, don't proceed but return -EAGAIN
+ * Exception: @avail = 0 for skipping the check.
*/
int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep,
- struct snd_urb_ctx *ctx, int idx)
+ struct snd_urb_ctx *ctx, int idx,
+ unsigned int avail)
{
- if (ctx->packet_size[idx])
- return ctx->packet_size[idx];
- else if (ep->sync_source)
- return slave_next_packet_size(ep);
+ unsigned int packet;
+
+ packet = ctx->packet_size[idx];
+ if (packet) {
+ if (avail && packet >= avail)
+ return -EAGAIN;
+ return packet;
+ }
+
+ if (ep->sync_source)
+ return slave_next_packet_size(ep, avail);
else
- return next_packet_size(ep);
+ return next_packet_size(ep, avail);
}
static void call_retire_callback(struct snd_usb_endpoint *ep,
@@ -263,7 +284,7 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep,
unsigned int length;
int counts;
- counts = snd_usb_endpoint_next_packet_size(ep, ctx, i);
+ counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, 0);
length = counts * ep->stride; /* number of silent bytes */
offset = offs * ep->stride + extra * i;
urb->iso_frame_desc[i].offset = offset;
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index a1099ec37e1c..1f1a72535a64 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -46,6 +46,7 @@ void snd_usb_endpoint_free_all(struct snd_usb_audio *chip);
int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep);
int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep,
- struct snd_urb_ctx *ctx, int idx);
+ struct snd_urb_ctx *ctx, int idx,
+ unsigned int avail);
#endif /* __USBAUDIO_ENDPOINT_H */
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index ec7eeb1b82b8..8ad48c35c559 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -1365,7 +1365,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
spin_lock_irqsave(&subs->lock, flags);
subs->frame_limit += ep->max_urb_frames;
for (i = 0; i < ctx->packets; i++) {
- counts = snd_usb_endpoint_next_packet_size(ep, ctx, i);
+ counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, 0);
/* set up descriptor */
urb->iso_frame_desc[i].offset = frames * stride;
urb->iso_frame_desc[i].length = counts * stride;