Commit d215f63d authored by Takashi Iwai's avatar Takashi Iwai
Browse files

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: default avatarTakashi Iwai <tiwai@suse.de>
parent bceee753
Loading
Loading
Loading
Loading
+36 −15
Original line number Diff line number Diff line
@@ -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;
+2 −1
Original line number Diff line number Diff line
@@ -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 */
+1 −1
Original line number Diff line number Diff line
@@ -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;