Commit d215f63d authored by Takashi Iwai's avatar Takashi Iwai

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.deSigned-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent bceee753
...@@ -148,18 +148,23 @@ int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep) ...@@ -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 * This won't be used for implicit feedback which takes the packet size
* returned from the sync source * 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 long flags;
unsigned int phase;
int ret; int ret;
if (ep->fill_max) if (ep->fill_max)
return ep->maxframesize; return ep->maxframesize;
spin_lock_irqsave(&ep->lock, flags); spin_lock_irqsave(&ep->lock, flags);
ep->phase = (ep->phase & 0xffff) phase = (ep->phase & 0xffff) + (ep->freqm << ep->datainterval);
+ (ep->freqm << ep->datainterval); ret = min(phase >> 16, ep->maxframesize);
ret = min(ep->phase >> 16, ep->maxframesize); if (avail && ret >= avail)
ret = -EAGAIN;
else
ep->phase = phase;
spin_unlock_irqrestore(&ep->lock, flags); spin_unlock_irqrestore(&ep->lock, flags);
return ret; return ret;
...@@ -169,20 +174,25 @@ static int slave_next_packet_size(struct snd_usb_endpoint *ep) ...@@ -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 * Return the number of samples to be sent in the next packet
* for adaptive and synchronous endpoints * 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; int ret;
if (ep->fill_max) if (ep->fill_max)
return ep->maxframesize; return ep->maxframesize;
ep->sample_accum += ep->sample_rem; sample_accum += ep->sample_rem;
if (ep->sample_accum >= ep->pps) { if (sample_accum >= ep->pps) {
ep->sample_accum -= ep->pps; sample_accum -= ep->pps;
ret = ep->packsize[1]; ret = ep->packsize[1];
} else { } else {
ret = ep->packsize[0]; ret = ep->packsize[0];
} }
if (avail && ret >= avail)
ret = -EAGAIN;
else
ep->sample_accum = sample_accum;
return ret; return ret;
} }
...@@ -190,16 +200,27 @@ static int next_packet_size(struct snd_usb_endpoint *ep) ...@@ -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 * snd_usb_endpoint_next_packet_size: Return the number of samples to be sent
* in the next packet * 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, 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]) unsigned int packet;
return ctx->packet_size[idx];
else if (ep->sync_source) packet = ctx->packet_size[idx];
return slave_next_packet_size(ep); if (packet) {
if (avail && packet >= avail)
return -EAGAIN;
return packet;
}
if (ep->sync_source)
return slave_next_packet_size(ep, avail);
else else
return next_packet_size(ep); return next_packet_size(ep, avail);
} }
static void call_retire_callback(struct snd_usb_endpoint *ep, static void call_retire_callback(struct snd_usb_endpoint *ep,
...@@ -263,7 +284,7 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep, ...@@ -263,7 +284,7 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep,
unsigned int length; unsigned int length;
int counts; 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 */ length = counts * ep->stride; /* number of silent bytes */
offset = offs * ep->stride + extra * i; offset = offs * ep->stride + extra * i;
urb->iso_frame_desc[i].offset = offset; urb->iso_frame_desc[i].offset = offset;
......
...@@ -46,6 +46,7 @@ void snd_usb_endpoint_free_all(struct snd_usb_audio *chip); ...@@ -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_implicit_feedback_sink(struct snd_usb_endpoint *ep);
int snd_usb_endpoint_next_packet_size(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 */ #endif /* __USBAUDIO_ENDPOINT_H */
...@@ -1365,7 +1365,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, ...@@ -1365,7 +1365,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
spin_lock_irqsave(&subs->lock, flags); spin_lock_irqsave(&subs->lock, flags);
subs->frame_limit += ep->max_urb_frames; subs->frame_limit += ep->max_urb_frames;
for (i = 0; i < ctx->packets; i++) { 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 */ /* set up descriptor */
urb->iso_frame_desc[i].offset = frames * stride; urb->iso_frame_desc[i].offset = frames * stride;
urb->iso_frame_desc[i].length = counts * stride; urb->iso_frame_desc[i].length = counts * stride;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment