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>
This commit is contained in:
Takashi Iwai 2021-09-29 10:08:41 +02:00
parent bceee75387
commit d215f63d49
3 changed files with 39 additions and 17 deletions

View File

@ -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;

View File

@ -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 */

View File

@ -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;