Skip to content

Commit

Permalink
ALSA: usb-audio: Check available frames for the next packet size
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Takashi Iwai committed Sep 30, 2021
1 parent bceee75 commit d215f63
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 17 deletions.
51 changes: 36 additions & 15 deletions sound/usb/endpoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -169,37 +174,53 @@ 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;
}

/*
* 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,
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion sound/usb/endpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
2 changes: 1 addition & 1 deletion sound/usb/pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit d215f63

Please sign in to comment.