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:
parent
bceee75387
commit
d215f63d49
@ -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)
|
|||||||
* 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)
|
|||||||
/*
|
/*
|
||||||
* 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,
|
|||||||
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);
|
|||||||
|
|
||||||
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,
|
|||||||
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;
|
||||||
|
Loading…
Reference in New Issue
Block a user